﻿<?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/rendong/category/13646.html</link><description /><language>zh-cn</language><lastBuildDate>Fri, 02 Mar 2007 07:17:06 GMT</lastBuildDate><pubDate>Fri, 02 Mar 2007 07:17:06 GMT</pubDate><ttl>60</ttl><item><title>[转]用Acegi为你的Spring应用加把锁</title><link>http://www.blogjava.net/rendong/archive/2006/12/24/89774.html</link><dc:creator>rendong</dc:creator><author>rendong</author><pubDate>Sun, 24 Dec 2006 12:05:00 GMT</pubDate><guid>http://www.blogjava.net/rendong/archive/2006/12/24/89774.html</guid><wfw:comment>http://www.blogjava.net/rendong/comments/89774.html</wfw:comment><comments>http://www.blogjava.net/rendong/archive/2006/12/24/89774.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/rendong/comments/commentRss/89774.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/rendong/services/trackbacks/89774.html</trackback:ping><description><![CDATA[
		<blockquote>
				<p>关键字 J2EE Spring Acegi</p>
				<h3>[简介]</h3>
				<p>对于一个典型的Web应用，完善的认证和授权机制是必不可少的，在SpringFramework中，Juergen Hoeller提供的范例JPetStore给了一些这方面的介绍，但还远远不够，Acegi是一个专门为SpringFramework提供安全机制的 项目，全称为Acegi Security System for Spring，当前版本为0.5.1，就其目前提供的功能，应该可以满足绝大多数应用的需求。</p>
				<p>本文的主要目的是希望能够说明如何在基于Spring构架的Web应用中使用Acegi，而不是详细介绍其中的每个接口、每个类。注意，即使对已经存在的Spring应用，通过下面介绍的步骤，也可以马上享受到Acegi提供的认证和授权。</p>
				<p>[基础工作] <br />在你的Web应用的lib中添加Acegi下载包中的acegi-security.jar</p>
				<p>[web.xml] <br />实现认证和授权的最常用的方法是通过filter，Acegi亦是如此，通常Acegi需要在web.xml添加以下5个filter:</p>
				<p>&lt;filter&gt; <br />&lt;filter-name&gt;Acegi Channel Processing Filter&lt;/filter-name&gt; <br />&lt;filter-class&gt;net.sf.acegisecurity.util.FilterToBeanProxy&lt;/filter-class&gt; <br />&lt;init-param&gt; <br />&lt;param-name&gt;targetClass&lt;/param-name&gt; <br />&lt;param-value&gt;net.sf.acegisecurity.securechannel.ChannelProcessingFilter&lt;/param-value&gt; <br />&lt;/init-param&gt; <br />&lt;/filter&gt; <br />&lt;filter&gt; <br />&lt;filter-name&gt;Acegi Authentication Processing Filter&lt;/filter-name&gt; <br />&lt;filter-class&gt;net.sf.acegisecurity.util.FilterToBeanProxy&lt;/filter-class&gt; <br />&lt;init-param&gt; <br />&lt;param-name&gt;targetClass&lt;/param-name&gt; <br />&lt;param-value&gt;net.sf.acegisecurity.ui.webapp.AuthenticationProcessingFilter&lt;/param-value&gt; <br />&lt;/init-param&gt; <br />&lt;/filter&gt; <br />&lt;filter&gt; <br />&lt;filter-name&gt;Acegi HTTP BASIC Authorization Filter&lt;/filter-name&gt; <br />&lt;filter-class&gt;net.sf.acegisecurity.util.FilterToBeanProxy&lt;/filter-class&gt; <br />&lt;init-param&gt; <br />&lt;param-name&gt;targetClass&lt;/param-name&gt; <br />&lt;param-value&gt;net.sf.acegisecurity.ui.basicauth.BasicProcessingFilter&lt;/param-value&gt; <br />&lt;/init-param&gt; <br />&lt;/filter&gt; <br />&lt;filter&gt; <br />&lt;filter-name&gt;Acegi Security System for Spring Auto Integration Filter&lt;/filter-name&gt; <br />&lt;filter-class&gt;net.sf.acegisecurity.ui.AutoIntegrationFilter&lt;/filter-class&gt; <br />&lt;/filter&gt; <br />&lt;filter&gt; <br />&lt;filter-name&gt;Acegi HTTP Request Security Filter&lt;/filter-name&gt; <br />&lt;filter-class&gt;net.sf.acegisecurity.util.FilterToBeanProxy&lt;/filter-class&gt; <br />&lt;init-param&gt; <br />&lt;param-name&gt;targetClass&lt;/param-name&gt; <br />&lt;param-value&gt;net.sf.acegisecurity.intercept.web.SecurityEnforcementFilter&lt;/param-value&gt; <br />&lt;/init-param&gt; <br />&lt;/filter&gt;</p>
				<p>最先引起迷惑的是net.sf.acegisecurity.util.FilterToBeanProxy，Acegi自己的文档上解释是： “What FilterToBeanProxy does is delegate the Filter’s methods through to a bean which is obtained from the <br />Spring application context. This enables the bean to benefit from the Spring application context lifecycle support and configuration flexibility.”，如希望深究的话，去看看源代码应该不难理解。</p>
				<p>再下来就是添加filter-mapping了： <br />&lt;filter-mapping&gt; <br />&lt;filter-name&gt;Acegi Channel Processing Filter&lt;/filter-name&gt; <br />&lt;url-pattern&gt;/*&lt;/url-pattern&gt; <br />&lt;/filter-mapping&gt; <br />&lt;filter-mapping&gt; <br />&lt;filter-name&gt;Acegi Authentication Processing Filter&lt;/filter-name&gt; <br />&lt;url-pattern&gt;/*&lt;/url-pattern&gt; <br />&lt;/filter-mapping&gt; <br />&lt;filter-mapping&gt; <br />&lt;filter-name&gt;Acegi HTTP BASIC Authorization Filter&lt;/filter-name&gt; <br />&lt;url-pattern&gt;/*&lt;/url-pattern&gt; <br />&lt;/filter-mapping&gt; <br />&lt;filter-mapping&gt; <br />&lt;filter-name&gt;Acegi Security System for Spring Auto Integration Filter&lt;/filter-name&gt; <br />&lt;url-pattern&gt;/*&lt;/url-pattern&gt; <br />&lt;/filter-mapping&gt; <br />&lt;filter-mapping&gt; <br />&lt;filter-name&gt;Acegi HTTP Request Security Filter&lt;/filter-name&gt; <br />&lt;url-pattern&gt;/*&lt;/url-pattern&gt; <br />&lt;/filter-mapping&gt;</p>
				<p>这里，需要注意以下两点： <br />1) 这几个filter的顺序是不能更改的，顺序不对将无法正常工作； <br />2) 如果你的应用不需要安全传输，如https，则将”Acegi Channel Processing Filter”相关内容注释掉即可； <br />3) 如果你的应用不需要Spring提供的远程访问机制，如Hessian and Burlap，将”Acegi HTTP BASIC Authorization <br />Filter”相关内容注释掉即可。</p>
				<p>[applicationContext.xml] <br />接下来就是要添加applicationContext.xml中的内容了，从刚才FilterToBeanFactory的解释可以看出，真正的filter都 <br />在Spring的applicationContext中管理：</p>
				<p>1) 首先，你的数据库中必须具有保存用户名和密码的table，Acegi要求table的schema必须如下：</p>
				<p>CREATE TABLE users ( <br />username VARCHAR(50) NOT NULL PRIMARY KEY, <br />password VARCHAR(50) NOT NULL, <br />enabled BIT NOT NULL <br />); <br />CREATE TABLE authorities ( <br />username VARCHAR(50) NOT NULL, <br />authority VARCHAR(50) NOT NULL <br />); <br />CREATE UNIQUE INDEX ix_auth_username ON authorities ( username, authority ); <br />ALTER TABLE authorities ADD CONSTRAINT fk_authorities_users foreign key (username) REFERENCES users <br />(username);</p>
				<p>2) 添加访问你的数据库的datasource和Acegi的jdbcDao，如下：</p>
				<p>&lt;bean id=”dataSource” class=”org.springframework.jdbc.datasource.DriverManagerDataSource”&gt; <br />&lt;property name=”driverClassName”&gt;&lt;value&gt;${jdbc.driverClassName}&lt;/value&gt;&lt;/property&gt; <br />&lt;property name=”url”&gt;&lt;value&gt;${jdbc.url}&lt;/value&gt;&lt;/property&gt; <br />&lt;property name=”username”&gt;&lt;value&gt;${jdbc.username}&lt;/value&gt;&lt;/property&gt; <br />&lt;property name=”password”&gt;&lt;value&gt;${jdbc.password}&lt;/value&gt;&lt;/property&gt; <br />&lt;/bean&gt; <br />&lt;bean id=”jdbcDaoImpl” class=”net.sf.acegisecurity.providers.dao.jdbc.JdbcDaoImpl”&gt; <br />&lt;property name=”dataSource”&gt;&lt;ref bean=”dataSource”/&gt;&lt;/property&gt; <br />&lt;/bean&gt;</p>
				<p>3) 添加DaoAuthenticationProvider:</p>
				<p>&lt;bean id=”daoAuthenticationProvider” class=”net.sf.acegisecurity.providers.dao.DaoAuthenticationProvider”&gt; <br />&lt;property name=”authenticationDao”&gt;&lt;ref bean=”authenticationDao”/&gt;&lt;/property&gt; <br />&lt;property name=”userCache”&gt;&lt;ref bean=”userCache”/&gt;&lt;/property&gt; <br />&lt;/bean&gt;</p>
				<p>&lt;bean id=”userCache” class=”net.sf.acegisecurity.providers.dao.cache.EhCacheBasedUserCache”&gt; <br />&lt;property name=”minutesToIdle”&gt;&lt;value&gt;5&lt;/value&gt;&lt;/property&gt; <br />&lt;/bean&gt;</p>
				<p>如果你需要对密码加密，则在daoAuthenticationProvider中加入：&lt;property name=”passwordEncoder”&gt;&lt;ref <br />bean=”passwordEncoder”/&gt;&lt;/property&gt;，Acegi提供了几种加密方法，详细情况可看包 <br />net.sf.acegisecurity.providers.encoding</p>
				<p>4) 添加authenticationManager:</p>
				<p>&lt;bean id=”authenticationManager” class=”net.sf.acegisecurity.providers.ProviderManager”&gt; <br />&lt;property name=”providers”&gt; <br />&lt;list&gt; <br />&lt;ref bean=”daoAuthenticationProvider”/&gt; <br />&lt;/list&gt; <br />&lt;/property&gt; <br />&lt;/bean&gt;</p>
				<p>5) 添加accessDecisionManager:</p>
				<p>&lt;bean id=”accessDecisionManager” class=”net.sf.acegisecurity.vote.AffirmativeBased”&gt; <br />&lt;property name=”allowIfAllAbstainDecisions”&gt; <br />&lt;value&gt;false&lt;/value&gt; <br />&lt;/property&gt; <br />&lt;property name=”decisionVoters”&gt; <br />&lt;list&gt;&lt;ref bean=”roleVoter”/&gt;&lt;/list&gt; <br />&lt;/property&gt; <br />&lt;/bean&gt; <br />&lt;bean id=”roleVoter” class=”net.sf.acegisecurity.vote.RoleVoter”/&gt;</p>
				<p>6) 添加authenticationProcessingFilterEntryPoint:</p>
				<p>&lt;bean id=”authenticationProcessingFilterEntryPoint” <br />class=”net.sf.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint”&gt; <br />&lt;property name=”loginFormUrl”&gt;&lt;value&gt;/acegilogin.jsp&lt;/value&gt;&lt;/property&gt; <br />&lt;property name=”forceHttps”&gt;&lt;value&gt;false&lt;/value&gt;&lt;/property&gt; <br />&lt;/bean&gt;</p>
				<p>其中acegilogin.jsp是登陆页面，一个最简单的登录页面如下：</p>
				<p>&lt;%@ taglib prefix=’c’ uri=’http://java.sun.com/jstl/core’ %&gt; <br />&lt;%@ page import=”net.sf.acegisecurity.ui.AbstractProcessingFilter” %&gt; <br />&lt;%@ page import=”net.sf.acegisecurity.AuthenticationException” %&gt; <br />&lt;html&gt; <br />&lt;head&gt; <br />&lt;title&gt;Login&lt;/title&gt; <br />&lt;/head&gt;</p>
				<p>&lt;body&gt; <br />&lt;h1&gt;Login&lt;/h1&gt; <br />&lt;form action=”&lt;c:url value=’j_acegi_security_check’/&gt;” method=”POST”&gt; <br />&lt;table&gt; <br />&lt;tr&gt;&lt;td&gt;User:&lt;/td&gt;&lt;td&gt;&lt;input type=’text’ name=’j_username’&gt;&lt;/td&gt;&lt;/tr&gt; <br />&lt;tr&gt;&lt;td&gt;Password:&lt;/td&gt;&lt;td&gt;&lt;input type=’password’ name=’j_password’&gt;&lt;/td&gt;&lt;/tr&gt; <br />&lt;tr&gt;&lt;td colspan=’2′&gt;&lt;input name=”submit” type=”submit”&gt;&lt;/td&gt;&lt;/tr&gt; <br />&lt;tr&gt;&lt;td colspan=’2′&gt;&lt;input name=”reset” type=”reset”&gt;&lt;/td&gt;&lt;/tr&gt; <br />&lt;/table&gt; <br />&lt;/form&gt; <br />&lt;/body&gt; <br />&lt;/html&gt;</p>
				<p>7) 添加filterInvocationInterceptor:</p>
				<p>&lt;bean id=”filterInvocationInterceptor” <br />class=”net.sf.acegisecurity.intercept.web.FilterSecurityInterceptor”&gt; <br />&lt;property name=”authenticationManager”&gt; <br />&lt;ref bean=”authenticationManager”/&gt; <br />&lt;/property&gt; <br />&lt;property name=”accessDecisionManager”&gt; <br />&lt;ref bean=”accessDecisionManager”/&gt; <br />&lt;/property&gt; <br />&lt;property name=”objectDefinitionSource”&gt; <br />&lt;value&gt; <br />CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON <br />\A/sec/administrator.*\Z=ROLE_SUPERVISOR <br />\A/sec/user.*\Z=ROLE_TELLER <br />&lt;/value&gt; <br />&lt;/property&gt; <br />&lt;/bean&gt;</p>
				<p>这里请注意，要objectDefinitionSource中定义哪些页面需要权限访问，需要根据自己的应用需求进行修改，我上面给出 <br />的定义的意思是这样的： <br />a. CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON意思是在比较请求路径时全部转换为小写 <br />b. \A/sec/administrator.*\Z=ROLE_SUPERVISOR意思是只有权限为ROLE_SUPERVISOR才能访问/sec/administrator*的页面 <br />c. \A/sec/user.*\Z=ROLE_TELLER意思是只有权限为ROLE_TELLER的用户才能访问/sec/user*的页面</p>
				<p>8) 添加securityEnforcementFilter:</p>
				<p>&lt;bean id=”securityEnforcementFilter” class=”net.sf.acegisecurity.intercept.web.SecurityEnforcementFilter”&gt; <br />&lt;property name=”filterSecurityInterceptor”&gt; <br />&lt;ref bean=”filterInvocationInterceptor”/&gt; <br />&lt;/property&gt; <br />&lt;property name=”authenticationEntryPoint”&gt; <br />&lt;ref bean=”authenticationProcessingFilterEntryPoint”/&gt; <br />&lt;/property&gt; <br />&lt;/bean&gt;</p>
				<p>9) 添加authenticationProcessingFilter:</p>
				<p>&lt;bean id=”authenticationProcessingFilter” <br />class=”net.sf.acegisecurity.ui.webapp.AuthenticationProcessingFilter”&gt; <br />&lt;property name=”authenticationManager”&gt; <br />&lt;ref bean=”authenticationManager”/&gt; <br />&lt;/property&gt; <br />&lt;property name=”authenticationFailureUrl”&gt; <br />&lt;value&gt;/loginerror.jsp&lt;/value&gt; <br />&lt;/property&gt; <br />&lt;property name=”defaultTargetUrl”&gt; <br />&lt;value&gt;/&lt;/value&gt; <br />&lt;/property&gt; <br />&lt;property name=”filterProcessesUrl”&gt; <br />&lt;value&gt;/j_acegi_security_check&lt;/value&gt; <br />&lt;/property&gt; <br />&lt;/bean&gt; <br />其中authenticationFailureUrl是认证失败的页面。</p>
				<p>10) 如果需要一些页面通过安全通道的话，添加下面的配置:</p>
				<p>&lt;bean id=”channelProcessingFilter” class=”net.sf.acegisecurity.securechannel.ChannelProcessingFilter”&gt; <br />&lt;property name=”channelDecisionManager”&gt; <br />&lt;ref bean=”channelDecisionManager”/&gt; <br />&lt;/property&gt; <br />&lt;property name=”filterInvocationDefinitionSource”&gt; <br />&lt;value&gt; <br />CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON <br />\A/sec/administrator.*\Z=REQUIRES_SECURE_CHANNEL <br />\A/acegilogin.jsp.*\Z=REQUIRES_SECURE_CHANNEL <br />\A/j_acegi_security_check.*\Z=REQUIRES_SECURE_CHANNEL <br />\A.*\Z=REQUIRES_INSECURE_CHANNEL <br />&lt;/value&gt; <br />&lt;/property&gt; <br />&lt;/bean&gt;</p>
				<p>&lt;bean id=”channelDecisionManager” class=”net.sf.acegisecurity.securechannel.ChannelDecisionManagerImpl”&gt; <br />&lt;property name=”channelProcessors”&gt; <br />&lt;list&gt; <br />&lt;ref bean=”secureChannelProcessor”/&gt; <br />&lt;ref bean=”insecureChannelProcessor”/&gt; <br />&lt;/list&gt; <br />&lt;/property&gt; <br />&lt;/bean&gt; <br />&lt;bean id=”secureChannelProcessor” class=”net.sf.acegisecurity.securechannel.SecureChannelProcessor”/&gt; <br />&lt;bean id=”insecureChannelProcessor” class=”net.sf.acegisecurity.securechannel.InsecureChannelProcessor”/&gt;</p>
				<p>[缺少了什么？] <br />Acegi目前提供了两种”secure object”，分别对页面和方法进行安全认证管理，我这里介绍的只是利用 <br />FilterSecurityInterceptor对访问页面的权限控制，除此之外，Acegi还提供了另外一个Interceptor– <br />MethodSecurityInterceptor，它结合runAsManager可实现对对象中的方法的权限控制，使用方法可参看Acegi自带的文档 <br />和contact范例。</p>
				<p>[最后要说的] <br />本来以为只是说明如何使用Acegi而已，应该非常简单，但真正写起来才发现想要条理清楚的理顺所有需要的bean还是很 <br />困难的，但愿我没有遗漏太多东西，如果我的文章有什么遗漏或错误的话，还请参看Acegi自带的quick-start范例，但请 <br />注意，这个范例是不能直接拿来用的。</p>
				<p>原文在这里：</p>
				<p>Trackback: <a href="http://tb.blog.csdn.net/TrackBack.aspx?PostId=54881">http://tb.blog.csdn.net/TrackBack.aspx?PostId=54881</a></p>
		</blockquote>
<img src ="http://www.blogjava.net/rendong/aggbug/89774.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/rendong/" target="_blank">rendong</a> 2006-12-24 20:05 <a href="http://www.blogjava.net/rendong/archive/2006/12/24/89774.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ThreadLocal的几种误区(转载 原作者jspark)</title><link>http://www.blogjava.net/rendong/archive/2006/08/05/61883.html</link><dc:creator>rendong</dc:creator><author>rendong</author><pubDate>Fri, 04 Aug 2006 16:45:00 GMT</pubDate><guid>http://www.blogjava.net/rendong/archive/2006/08/05/61883.html</guid><wfw:comment>http://www.blogjava.net/rendong/comments/61883.html</wfw:comment><comments>http://www.blogjava.net/rendong/archive/2006/08/05/61883.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/rendong/comments/commentRss/61883.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/rendong/services/trackbacks/61883.html</trackback:ping><description><![CDATA[最近由于需要用到ThreadLocal，在网上搜索了一些相关资料，发现对ThreadLocal经常会有下面几种<u>误解</u><br /><br /> 一、ThreadLocal是java线程的一个实现<br />      ThreadLocal的确是和java线程有关，不过它并不是java线程的一个实现，它只是用来维护本地变量。针对每个线程，提供自己的变量版本，主要是为了避免线程冲突，每个线程维护自己的版本。彼此独立，修改不会影响到对方。
<p> 二、ThreadLocal是相对于每个session的</p><p>        ThreadLocal顾名思义，是针对线程。在java web编程上，每个用户从开始到会话结束，都有自己的一个session标识。但是ThreadLocal并不是在会话层上。其实，Threadlocal是独立于用户session的。它是一种服务器端行为，当服务器每生成一个新的线程时，就会维护自己的ThreadLocal。对于这个误解，个人认为应该是开发人员在本地基于一些应用服务器测试的结果。众所周知，一般的应用服务器都会维护一套线程池，也就是说，对于每次访问，并不一定就新生成一个线程。而是自己有一个线程缓存池。对于访问，先从缓存池里面找到已有的线程，如果已经用光，才去新生成新的线程。所以，由于开发人员自己在测试时，一般只有他自己在测，这样服务器的负担很小，这样导致每次访问可能是共用同样一个线程，导致会有这样的误解：每个session有一个ThreadLocal</p><p> 三、ThreadLocal是相对于每个线程的，用户每次访问会有新的ThreadLocal</p><p>  理论上来说，ThreadLocal是的确是相对于每个线程，每个线程会有自己的ThreadLocal。但是上面已经讲到，一般的应用服务器都会维护一套线程池。因此，不同用户访问，可能会接受到同样的线程。因此，在做基于TheadLocal时，需要谨慎，避免出现ThreadLocal变量的缓存，导致其他线程访问到本线程变量</p><p> 四、对每个用户访问，ThreadLocal可以多用<br />        可以说，ThreadLocal是一把双刃剑，用得来的话可以起到非常好的效果。但是，ThreadLocal如果用得不好，就会跟全局变量一样。代码不能重用，不能独立测试。因为，一些本来可以重用的类，现在依赖于ThreadLocal变量。如果在其他没有ThreadLocal场合，这些类就变得不可用了。个人觉得ThreadLocal用得很好的几个应用场合，值得参考</p><p>  1、存放当前session用户：quake want的jert</p><p>  2、存放一些context变量，比如webwork的ActionContext</p><p>  3、存放session，比如Spring hibernate orm的session</p><img src ="http://www.blogjava.net/rendong/aggbug/61883.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/rendong/" target="_blank">rendong</a> 2006-08-05 00:45 <a href="http://www.blogjava.net/rendong/archive/2006/08/05/61883.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Spring中的destroy-method="close"(转自JavaEye)</title><link>http://www.blogjava.net/rendong/archive/2006/08/04/61797.html</link><dc:creator>rendong</dc:creator><author>rendong</author><pubDate>Fri, 04 Aug 2006 09:14:00 GMT</pubDate><guid>http://www.blogjava.net/rendong/archive/2006/08/04/61797.html</guid><wfw:comment>http://www.blogjava.net/rendong/comments/61797.html</wfw:comment><comments>http://www.blogjava.net/rendong/archive/2006/08/04/61797.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/rendong/comments/commentRss/61797.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/rendong/services/trackbacks/61797.html</trackback:ping><description><![CDATA[         <font face="Courier New,Courier,Monospace">究竟Spring在何时调用destroy-method="close" 这个方法close()呢？终于借助JavaEye找到了答案，原来如果Spring不在Web Container或是EJB Container中的时候，这个方法还是需要我们自己来调用的，具体就是调用BeanFactory的destroySingletons()方法，文档上的“自动调用”这几个字真是害我不浅呀，原来自动也是通过Web Container或是EJB Container才可以自动，具体做法就是要实现ServletContextListener这个接口，Spring中已经有具体的实现了：</font><div><font face="Courier New,Courier,Monospace"></font> </div><div><font face="Courier New,Courier,Monospace"><span style="FONT-WEIGHT: bold; COLOR: #990066" ?="">public</span><span style="FONT-WEIGHT: bold; COLOR: #990066" ?="">class</span> ContextLoaderListener <span style="FONT-WEIGHT: bold; COLOR: #990066" ?="">implements</span> ServletContextListener <span style="COLOR: #000000">{</span> <br />        <span style="FONT-WEIGHT: bold; COLOR: #990066" ?="">private</span> ContextLoader contextLoader; <br />        </font><font face="Courier New,Courier,Monospace"><span style="COLOR: #6666ff">/** <br />        * Initialize the root web application context. <br />        */</span><br />        <span style="FONT-WEIGHT: bold; COLOR: #990066" ?="">public</span><span style="FONT-WEIGHT: bold; COLOR: #990066" ?="">void</span> contextInitialized<span style="COLOR: #000000">(</span>ServletContextEvent event<span style="COLOR: #000000">)</span><span style="COLOR: #000000">{</span><br />                this.<span style="COLOR: #000000">contextLoader</span> = createContextLoader<span style="COLOR: #000000">(</span><span style="COLOR: #000000">)</span>; <br />                this.<span style="COLOR: #000000">contextLoader</span>.<span style="COLOR: #000000">initWebApplicationContext</span><span style="COLOR: #000000">(</span>event.<span style="COLOR: #000000">getServletContext</span><span style="COLOR: #000000">(</span><span style="COLOR: #000000">)</span><span style="COLOR: #000000">)</span>; <br />        <span style="COLOR: #000000">}</span> <br />        </font><font face="Courier New,Courier,Monospace"><span style="COLOR: #6666ff">/** <br />        * Create the ContextLoader to use. Can be overridden in subclasses. <br />        * @return the new ContextLoader <br />        */</span><br />        <span style="FONT-WEIGHT: bold; COLOR: #990066" ?="">protected</span> ContextLoader createContextLoader<span style="COLOR: #000000">(</span><span style="COLOR: #000000">)</span><span style="COLOR: #000000">{</span><br />                <span style="FONT-WEIGHT: bold; COLOR: #990066" ?="">return</span><span style="FONT-WEIGHT: bold; COLOR: #990066" ?="">new</span> ContextLoader<span style="COLOR: #000000">(</span><span style="COLOR: #000000">)</span>; <br />        <span style="COLOR: #000000">}</span> <br />        </font><font face="Courier New,Courier,Monospace"><span style="COLOR: #6666ff">/** <br />        * Return the ContextLoader used by this listener. <br />        */</span><br />        <span style="FONT-WEIGHT: bold; COLOR: #990066" ?="">public</span> ContextLoader getContextLoader<span style="COLOR: #000000">(</span><span style="COLOR: #000000">)</span><span style="COLOR: #000000">{</span><br />                <span style="FONT-WEIGHT: bold; COLOR: #990066" ?="">return</span> contextLoader; <br />        <span style="COLOR: #000000">}</span> <br />        </font><font face="Courier New,Courier,Monospace"><span style="COLOR: #6666ff">/** <br />        * Close the root web application context. <br />        */</span><br />        <span style="FONT-WEIGHT: bold; COLOR: #990066" ?="">public</span><span style="FONT-WEIGHT: bold; COLOR: #990066" ?="">void</span> contextDestroyed<span style="COLOR: #000000">(</span>ServletContextEvent event<span style="COLOR: #000000">)</span><span style="COLOR: #000000">{</span><br />                this.<span style="COLOR: #000000">contextLoader</span>.<span style="COLOR: #000000">closeWebApplicationContext</span><span style="COLOR: #000000">(</span>event.<span style="COLOR: #000000">getServletContext</span><span style="COLOR: #000000">(</span><span style="COLOR: #000000">)</span><span style="COLOR: #000000">)</span>; <br />        <span style="COLOR: #000000">}</span><br /><br /><span style="COLOR: #000000">}</span><br /></font></div><div><font face="Courier New,Courier,Monospace">当tomcat关闭的时候会<span style="FONT-WEIGHT: bold">自动</span>调用contextDestroyed(ServletContextEvent event)这个方法。在看一下contextLoader的closeWebApplicationContext方法： </font></div><div><font face="Courier New,Courier,Monospace"></font> </div><div><font face="Courier New,Courier,Monospace"><span style="FONT-WEIGHT: bold; COLOR: #990066" ?="">public</span><span style="FONT-WEIGHT: bold; COLOR: #990066" ?="">void</span> closeWebApplicationContext<span style="COLOR: #000000">(</span>ServletContext servletContext<span style="COLOR: #000000">)</span><span style="FONT-WEIGHT: bold; COLOR: #990066" ?="">throws</span> ApplicationContextException <span style="COLOR: #000000">{</span><br />                servletContext.<span style="COLOR: #000000">log</span><span style="COLOR: #000000">(</span>"Closing root WebApplicationContext"<span style="COLOR: #000000">)</span>; <br />                <span style="COLOR: #aaaadd" ?="">Object</span> wac = servletContext.<span style="COLOR: #000000">getAttribute</span><span style="COLOR: #000000">(</span>WebApplicationContext.<span style="COLOR: #000000">ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE</span><span style="COLOR: #000000">)</span>; <br />                <span style="FONT-WEIGHT: bold; COLOR: #990066" ?="">if</span><span style="COLOR: #000000">(</span>wac instanceof ConfigurableApplicationContext<span style="COLOR: #000000">)</span><span style="COLOR: #000000">{</span><br />                        <span style="COLOR: #000000">(</span><span style="COLOR: #000000">(</span>ConfigurableApplicationContext<span style="COLOR: #000000">)</span> wac<span style="COLOR: #000000">)</span>.<span style="COLOR: #000000">close</span><span style="COLOR: #000000">(</span><span style="COLOR: #000000">)</span>; <br />                <span style="COLOR: #000000">}</span><br />        <span style="COLOR: #000000">}</span><br /></font></div><div><font face="Courier New,Courier,Monospace">AbstractApplicationContext.Close这个方法是要你自己调用的，在程序要结束的时候保证调用这个close方法，在这里的话就是由Listener来保证tomcat退出的时候调用close方法。 <br />AbstractApplicationContext.Close的代码 ：</font></div><div><font face="Courier New,Courier,Monospace"></font> </div><div><font face="Courier New,Courier,Monospace"><span style="FONT-WEIGHT: bold; COLOR: #990066" ?="">public</span><span style="FONT-WEIGHT: bold; COLOR: #990066" ?="">void</span> close<span style="COLOR: #000000">(</span><span style="COLOR: #000000">)</span><span style="COLOR: #000000">{</span><br />                logger.<span style="COLOR: #000000">info</span><span style="COLOR: #000000">(</span>"Closing application context <span style="COLOR: #000000">[</span>" + getDisplayName<span style="COLOR: #000000">(</span><span style="COLOR: #000000">)</span> + "<span style="COLOR: #000000">]</span>"<span style="COLOR: #000000">)</span>; <br /><br />                <span style="COLOR: #6666ff">// Destroy all cached singletons in this context,</span><br />                <span style="COLOR: #6666ff">// invoking DisposableBean.destroy and/or "destroy-method".</span><br />                getBeanFactory<span style="COLOR: #000000">(</span><span style="COLOR: #000000">)</span>.<span style="COLOR: #000000">destroySingletons</span><span style="COLOR: #000000">(</span><span style="COLOR: #000000">)</span>; <br />                <span style="COLOR: #6666ff">// publish corresponding event</span><br />                publishEvent<span style="COLOR: #000000">(</span><span style="FONT-WEIGHT: bold; COLOR: #990066" ?="">new</span> ContextClosedEvent<span style="COLOR: #000000">(</span>this<span style="COLOR: #000000">)</span><span style="COLOR: #000000">)</span>; <br />        <span style="COLOR: #000000">}</span></font></div><p><span style="COLOR: #000000"><font face="Courier New,Courier,Monospace">最终还是调用到了getBeanFactory<span style="COLOR: #000000">(</span><span style="COLOR: #000000">)</span>.<span style="COLOR: #000000">destroySingletons</span><span style="COLOR: #000000">(</span><span style="COLOR: #000000">)</span>; 看来，没有容器，我们还是需要自己来搞定这个方法的调用的 ！</font></span></p><img src ="http://www.blogjava.net/rendong/aggbug/61797.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/rendong/" target="_blank">rendong</a> 2006-08-04 17:14 <a href="http://www.blogjava.net/rendong/archive/2006/08/04/61797.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>在Struts和Hibernate之间搭起桥梁 --zt (转)</title><link>http://www.blogjava.net/rendong/archive/2006/08/02/61268.html</link><dc:creator>rendong</dc:creator><author>rendong</author><pubDate>Tue, 01 Aug 2006 17:01:00 GMT</pubDate><guid>http://www.blogjava.net/rendong/archive/2006/08/02/61268.html</guid><wfw:comment>http://www.blogjava.net/rendong/comments/61268.html</wfw:comment><comments>http://www.blogjava.net/rendong/archive/2006/08/02/61268.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/rendong/comments/commentRss/61268.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/rendong/services/trackbacks/61268.html</trackback:ping><description><![CDATA[
		<span class="tpc_content">
				<font color="#993300">
						<strong>
								<span style="FONT-SIZE: 16px">摘要</span>
								<br />
								<br />
						</strong>
						<font size="2">Hibernate和struts是当前市面上几个最流行的开源的库之一。它们很有效率，是程序员在开发Java企业应用，挑选几个竞争的库的首选。虽然它们经常被一起应用，但是Hibernate的设计目标并不是和Struts一起使用，而Struts在Hibernate诞生好多年之前就发布了。为了让它们在一起工作，仍然有很多挑战。这篇文章点明了Struts和Hibernate之间的一些鸿沟，尤其关系到面向对象建模方面。文章也描述了如何在两者间搭起桥梁，给出了一个基于扩展Struts的解决方案。所有的基于Struts和Hibernate构建的Web应用都能从这个通用的扩展中获益。<br /><br />在Hibernate in Action（Manning,2004十月）这本书里，作者Christian Bauer和Gavin King揭示了面向对象世界的模型和关系数据模型，两个世界的范例是不一致的。Hibernate非常成功地在存储层(persistence Layer)将两者粘合在一起。但是领域模型(domain model)(也就是Model-View-Controller的model layer)和HTML页面(MVC的View Layer)仍然存在不一致。在这篇文章中，我们将检查这种不一致，并且探索解决的方案。<br /><br /></font>
						<strong>
								<span style="FONT-SIZE: 16px">范例不一致的再发现</span>
						</strong>
						<br />
						<br />
						<font size="2">让我们先看一个经典的parent-child关系例子（看下面的代码）：product和category。Category类定义了一个类型为long的标示符id和一个类型为String的属性name。Product类也有一个类型为long的标示符id和一个类型为Category的属性category，表示了多对一的关系（也就是说很多product可以属于一个Category)<br /><br /></font>
				</font>
				<pre class="overflow">
						<font color="#993300" size="2">/**<br />* @hibernate.class table="CATEGORY"<br />*/ <br />public class Category {<br />   private Long id;<br /><br />   private String name;<br /><br />   /**<br />    * @hibernate.id generator-class="native" column="CATEGORY_ID"<br />    */<br />   public Long getId() {<br />      return id;<br />   }<br /><br />   public void setId(Long id) {<br />      this.id = id;<br />   }<br /><br />   /**<br />    * @hibernate.property column="NAME"<br />    */<br />   public String getName() {<br />      return name;<br />   }<br /><br />   public void setName(Long name) {<br />      this.name = name;<br />   }<br />}<br /><br />/**<br />* @hibernate.class table="PRODUCT"<br />*/<br />public class Product {<br />   private Long id;<br />   private Category category;<br /><br />   /**<br />    * @hibernate.id generator-class="native" column="PRODUCT_ID"<br />    */<br />   public Long getId() {<br />      return id;<br />   }<br /><br />   public void setId(Long id) {<br />      this.id = id;<br />   }<br /><br />   /**<br />    * @hibernate.many-to-one<br />    * column="CATEGORY_ID"<br />    * class="Category"<br />    * cascade="none"<br />    * not-null="false"<br />    */<br />   public Category getCategory() {<br />      return category;<br />   }<br /><br />   public void setCategory(Category category) {<br />      this.category = category;<br />   }<br />}</font>
				</pre>
				<br />
				<br />
				<br />
				<font color="#993300" size="2">我们希望一个product可以被更改category，所以我们的HTML提供了一个下拉框列出所有Category。<br /><br /></font>
				<pre class="overflow">
						<font color="#993300" size="2">&lt;select name="categoryId"&gt;<br />   &lt;option value=""&gt;No Category&lt;/option&gt;<br />   &lt;option value="1"&gt;Category 1&lt;/option&gt;<br />   &lt;option value="2"&gt;Category 2&lt;/option&gt;<br />   &lt;option value="3"&gt;Category 3&lt;/option&gt;<br />&lt;/select&gt;</font>
				</pre>
				<br />
				<br />
				<br />
				<font color="#993300" size="2">这里我们看出了两者的不一致：在Product领域对象里，category属性是Category类型，但是ProductForm只有一个类型为long的categoryId。这种不匹配不但增加了不一致，而且导致了不必要的代码进行primitive type的标示符和对应的对象之间的转换。<br /><br />这种不一致部分是由于HTML Form自己引起的：它只代表了一种关系模型，不能代表面向对象的模型。面向对象和关系模型的不一致在存储层由对象关系映射(O/RM)解决。但是类似的问题在表示层(view layer)仍然存在。解决的关键是让他们一起无缝地工作。<br /><br /><strong><span style="FONT-SIZE: 16px">Struts的功能和局限</span></strong><br /><br />幸运的是，Struts能够生成和解释内嵌的对象属性。Category下拉框可以用Struts page-construction(html) tag library：<br /><br /></font>
				<pre class="overflow">
						<font color="#993300" size="2">&lt;html:select property="category.id"&gt;<br />   &lt;option value=""&gt;No Category&lt;/option&gt;<br />   &lt;html:options collection="categories" property="id" labelProperty="name"/&gt;<br />&lt;/html:select&gt;<br /></font>
				</pre>
				<br />
				<br />
				<font color="#993300" size="2">我们假设categories是Category对象的一个list。所以现在我们要修改ProductForm，让它变得更加“面向对象”，我们要修改ProductForm的categoryId，改成类型为Category的category。这种改变会导致在Product和ProductForm之间复制属性的工作更加繁琐，因为两者有相同的属性。<br /><br /></font>
				<pre class="overflow">
						<font color="#993300" size="2">public class ProductForm extends ActionForm {<br />     private Long id;<br />     private Category category;<br />     ...<br />} </font>
				</pre>
				<br />
				<br />
				<br />
				<font color="#993300" size="2">当我们完成剩余的Struts Action, configuration, validator, jsp, hibernate层后，开始测试，我们马上在访问ProductForm.category.id时遇到了NullPointerException。这是预料中的！因为ProductForm.category还没有被设置，同时，Hibernate也会将多对一所联系的对象引用设为空（如果database field为空指）（译者：这里指Hiberate将product.category为Null，如果该Product没有联系到任何category)。Struts要求所有的对象在显示(生成HTML Form)和传播(提交HTML FORM)之前被建立。<br /><br />让我们看看如何用ActionForm.reset()来架起桥梁。<br /><br /><strong><span style="FONT-SIZE: 16px">（并非如此）臭名昭著的Struts ActionForm</span></strong><br /><br />在我第一个星期接触Struts的时候，我最大的一个疑问就是：为什么我必须为Properties, getter方法, setter方法保持几乎完全相同的两份copy, 一份在ActionForm Bean, 一份在DomainObject。这个繁琐的步骤成了Struts社区最主要的抱怨之一。<br /><br />以我的观点，ActionForm存在有原因的。首先，它们可以区别于Domain Object因为他们但当了不同的角色。在MVC模式下，Domain Object是Model层的一个部分，ActionForm是View层的。因为Webpage的Field和Database的Field可能不一样，某些特制的转换是常见的。第二，ActionForm.validate()方法可以定义非常好用的验证规则。第三，可能有其他的，特定的View行为，但是又不想在domain layer实现，特别当persistence framework来管理domain object的时候。<br /><br /><strong>提交Form</strong><br /><br />让我们来用ActionForm内有的方法-reset()-来解决view和model之间的不一致。这个reset()方法是在ActionForm在被Struts Controller Servlet处理request时候复制ActionForm属性之前调用的。这个方法最常见的使用是：checkbox必须被显式地设为false，让没有被选中的checkbox被正确识别。Reset()也是一个初始化用于view rending对象的合适地方。代码看起来是这样的：<br /><br /></font>
				<pre class="overflow">
						<font color="#993300" size="2">public class ProductForm extends ActionForm {<br />     private Long id;<br />     private Category category;<br />     ...<br />     public void reset(ActionMapping mapping, HttpServletRequest request) <br />     {<br />        super.reset( mapping, request );<br />        if ( category == null ) { category = new Category(); }<br />     }<br />} </font>
				</pre>
				<br />
				<br />
				<br />
				<font color="#993300" size="2">Struts在使用用户提交的值填写ProductForm之前，Struts会调用reset()，这样category属性将会被初始化。请注意，你必须检查category看它是不是null，后面我们会讨论这个。<br /><br /><strong>编辑Form</strong><br /><br />到目前为止，我们已经解决了form提交时候的问题。但是当我们在生成form页面的时候呢？Html:select tag也希望有一个非空的引用，所以我们将在form生成页面之前调用reset()。我们在action类里加入了一行:<br /><br /></font>
				<pre class="overflow">
						<font color="#993300" size="2">public class EditProductAction extends Action {<br />     public final ActionForward execute( ActionMapping mapping, ActionForm form, <br />        HttpServletRequest request, HttpServletResponse response ) throws Exception<br />     {<br />        ...<br />        Product product = createOrLoadProduct();<br />        ProductForm productForm = (ProductForm)form;<br />        PropertyUtils.copyProperties( productForm, product );<br />        productForm.reset( mapping, request );<br />        ...<br />     }<br />} </font>
				</pre>
				<br />
				<br />
				<br />
				<font color="#993300" size="2">我假设读者已经对action类和Jakarta commons Beanutils包非常熟悉了。CreateOrLoadProduct()建立了一个新的Product实例或者从数据库里载入一个已有的实例，具体取决于这个action是建立或者修改Product的。ProductForm被赋值后（译者：也就是调用PropertyUtils.copyProperties后），productForm.category已经从Product.category复制过来了（译者：实际上只是复制了category对象引用，并没有开销），然后，ProductForm就能用来生成页面了。我们同时也必须保证：不覆盖已经被Hibernate载入的对象，所以我们必须检查(category)是不是为null。<br /><br />因为reset()方法是在ActionForm中定义的，我们可以把上述代码放入一个superclass，比如CommonEditAction，来处理这些事情：<br />    <br /></font>
				<pre class="overflow">
						<font color="#993300" size="2">      Product product = createOrLoadProduct();<br />        PropertyUtils.copyProperties( form, product );<br />        form.reset( mapping, request );</font>
				</pre>
				<br />
				<br />
				<font color="#993300" size="2">如果你需要一个只读的Form, 你有两个选择: 第一检查所联系的jsp对象是不是null, 第二复制domain对象到ActionForm之后调用Reset()<br /><br /><strong>保存domain对象</strong><br /><br />我们解决了提交Form和生成Form页面的问题, 所以Struts可以满足了。但是Hibernate呢？当用户选择了一个null ID option – 在我们的例子中“no category”option- 并且提交form, productForm.category指向一个新建立的hibernate对象，id为null。当category属性从ProductForm复制到Hibernate控制的Product对象并且存储时，Hibernate会抱怨product.category是一个临时对象，需要在Product存储前先被存储。当然，我们知道它是Null，并且不需要被存储。所以我们需要将product.category置为Null，然后Hibernate就能存储Product了（译者：在这种情况下，数据库product.category被设成空值）。我们也不希望改变Hibernate的工作方式，所以我们选择在复制到Domain对象之前清理这些临时对象，我们在ProductForm中加了一个方法:<br /><br /></font>
				<pre class="overflow">
						<font color="#993300" size="2">public class ProductForm extends ActionForm {<br />     private Long id;<br />     private Category category;<br />     ...<br />     public void reset(ActionMapping mapping, HttpServletRequest request) {<br />        super.reset( mapping, request );<br />        if ( category == null ) { category = new Category(); }<br />     }<br /><br />     public void cleanupEmptyObjects() {<br />        if ( category.getId() == null ) { category = null; }<br />     }<br />}</font>
				</pre>
				<br />
				<br />
				<br />
				<font color="#993300" size="2">我们在copyProperties之前清理掉这些临时对象，所以如果ProductForm.category只是用来放Null的，则将ProductForm.category置为Null。然后Domain对象的category也会被设成null:<br /><br /></font>
				<pre class="overflow">
						<font color="#993300" size="2">public class SaveProductAction extends Action {<br />     public final ActionForward execute( ActionMapping mapping, ActionForm form, <br />        HttpServletRequest request, HttpServletResponse response ) throws Exception<br />     {<br />        ...<br />        Product product = new Product();<br />        ((ProductForm)form).cleanupEmptyObjects();<br />        PropertyUtils.copyProperties( product, form );<br />        SaveProduct( product );<br />        ...<br />     }<br />} </font>
				</pre>
				<br />
				<br />
				<br />
				<font size="2">
						<font color="#993300">
								<strong>
										<span style="FONT-SIZE: 16px">一对多关系</span>
								</strong>
								<br />
								<br />我还没有解决Category到Product的一对多关系。我们把它加入到Category的Metadata中：<br /><br /></font>
				</font>
				<pre class="overflow">
						<font color="#993300" size="2">public class Category {<br />     ...<br />     private Set products;<br />     ...<br /><br />     /**<br />      * @hibernate.set<br />      * table="PRODUCT"<br />      * lazy="true"<br />      * outer-join="auto"<br />      * inverse="true"<br />      * cascade="all-delete-orphan"<br />      * <br />      * @hibernate.collection-key<br />      * column="CATEGORY_ID"<br />      * <br />      * @hibernate.collection-one-to-many<br />      * class="Product"<br />      */<br />     public Set getProducts() {<br />        return products;<br />     }<br /><br />     public void setProducts(Set products) {<br />        this.products = products;<br />     }<br />} </font>
				</pre>
				<br />
				<br />
				<font size="2">
						<font color="#993300">
								<strong>注意：</strong>Hibernate的cascade属性为all-delete-orphan表明：Hibernate需要在存储包含的Category对象时候，自动存储Product对象。和parent对象一起存储child对象的情况并不常见，常见的是：分别控制child的存储和parent的存储。在我们的例子中，我们可以容易地做到这一点，如果我们允许用户在同一个html page编辑Category和ProductS。用set表示Products是非常直观的：<br /><br /></font>
				</font>
				<pre class="overflow">
						<font color="#993300" size="2">public class CategoryForm extends ActionForm {<br />     private Set productForms;<br />     ...<br />     public void reset(ActionMapping mapping, HttpServletRequest request) {<br />        super.reset( mapping, request );<br /><br />        for ( int i = 0; i &lt; MAX_PRODUCT_NUM_ON_PAGE; i++ ) {<br />           ProductForm productForm = new ProductForm();<br />           productForm.reset( mapping, request );<br />           productForms.add( productForm );<br />        }<br />     }<br /><br />     public void cleanupEmptyObjects() {<br />        for ( Iterator i = productForms.iterator(); i.hasNext(); ) {<br />           ProductForm productForm = (ProductForm) i.next();<br />           productForm.cleanupEmptyObjects();<br />        }<br />     }<br />} </font>
				</pre>
				<br />
				<br />
				<br />
				<font size="2">
						<font color="#993300">
								<strong>更进一步</strong>
								<br />我们已经可以察看,编辑,提交forms,并且存储相关的objects,但是为所有的ActionForm类定义CleanupEmptyObjects()和reset()方法是个累赘。我们将用一个抽象的ActionForm来完成协助完成这些工作。<br /><br />作为通用的实现，我们必须遍历所有的Hibernate管理的domain对象，发现他们的identifier，并且测试id值。幸运的是：org.hibernate.metadata包已经有两个Utility类能取出domain对象的元数据。我们用ClassMetadata类检查这个object是不是Hibernate管理的。如果是：我们把它们的id Value取出来。我们用了Jakarta Commons Beanutils包来协助JavaBean元数据的操作。<br /><br /></font>
				</font>
				<pre class="overflow">
						<font color="#993300" size="2">import java.beans.PropertyDescriptor;<br />import org.apache.commons.beanutils.PropertyUtils;<br />import org.hibernate.metadata.ClassMetadata;<br /><br />public abstract class AbstractForm extends ActionForm {<br />   public void reset(ActionMapping mapping, HttpServletRequest request) {<br />      super.reset( mapping, request );<br /><br />      // Get PropertyDescriptor of all bean properties<br />      PropertyDescriptor descriptors[] = <br />         PropertyUtils.getPropertyDescriptors( this );<br /><br />      for ( int i = 0; i &lt; descriptors.length; i++ ) {<br />         Class propClass = descriptors<i>.getPropertyType();<br /><br />         ClassMetadata classMetadata = HibernateUtil.getSessionFactory()<br />            .getClassMetadata( propClass );<br /><br />         if ( classMetadata != null ) {   // This is a Hibernate object<br />            String propName = descriptors<i>.getName();<br />            Object propValue = PropertyUtils.getProperty( this, propName );<br /><br />            // Evaluate property, create new instance if it is null<br />            if ( propValue == null ) {<br />               PropertyUtils.setProperty( this, propName, propClass.newInstance() );<br />            }<br />         }<br />      }<br />   }<br /><br />   public void cleanupEmptyObjects() {<br />      // Get PropertyDescriptor of all bean properties<br />      PropertyDescriptor descriptors[] =<br />      PropertyUtils.getPropertyDescriptors( this );<br /><br />      for ( int i = 0; i &lt; descriptors.length; i++ ) {<br />         Class propClass = descriptors<i>.getPropertyType();<br />         ClassMetadata classMetadata = HibernateUtil.getSessionFactory()<br />            .getClassMetadata( propClass );<br /><br />         if ( classMetadata != null ) {   // This is a Hibernate object<br />            Serializable id = classMetadata.getIdentifier( this, EntityMode.POJO );<br /><br />            // If the object id has not been set, release the object.<br />            // Define application specific rules of not-set id here,<br />            // e.g. id == null, id == 0, etc.<br />            if ( id == null ) {<br />               String propName = descriptors<i>.getName();<br />               PropertyUtils.setProperty( this, propName, null );<br />            }<br /><br /><br />         }<br />      }<br />   }<br />} </i></i></i></i></font>
				</pre>
				<br />
				<br />
				<font color="#993300" size="2">为了让代码可读，我们省略了Exception的处理代码。<br /><br />我们的新AbstractForm类从Struts的ActionForm类继承，并且提供了通用行为：reset和cleanup多对一关联对象。当这个关系是相反的话（也就是一对多关系），那么每个例子将会有所不同，类似在Abstract类里实现是比较好的办法。<br /><br /><strong><span style="FONT-SIZE: 16px">总结</span></strong><br /><br />Struts和Hibernate是非常流行和强大的框架，他们可以有效地相互合作，并且弥补domain模型和MVC视图(view)之间的差别。这篇文章讨论一个解决Struts/Hibernate Project的通用的方案，并且不需要大量修改已经有的代码。</font>
		</span>
<img src ="http://www.blogjava.net/rendong/aggbug/61268.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/rendong/" target="_blank">rendong</a> 2006-08-02 01:01 <a href="http://www.blogjava.net/rendong/archive/2006/08/02/61268.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>spring配置文件简化(转 http://www.xin7dian.com/simple/index.php?t146.html)</title><link>http://www.blogjava.net/rendong/archive/2006/08/02/61267.html</link><dc:creator>rendong</dc:creator><author>rendong</author><pubDate>Tue, 01 Aug 2006 16:57:00 GMT</pubDate><guid>http://www.blogjava.net/rendong/archive/2006/08/02/61267.html</guid><wfw:comment>http://www.blogjava.net/rendong/comments/61267.html</wfw:comment><comments>http://www.blogjava.net/rendong/archive/2006/08/02/61267.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/rendong/comments/commentRss/61267.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/rendong/services/trackbacks/61267.html</trackback:ping><description><![CDATA[人人都爱Spring加Hibernate。<br />    但Spring MVC+hibernate的Sample如Appfuse的代码却算不得最简洁优美好读，如果在自己的项目中继续发挥我们最擅长的依样画葫芦大法，美好愿望未必会实现。 <br />     所以，Pramatic精神不灭。这个系列就是探寻最适合自己的Spring+Hibernate模式。<br />    <br />                              <strong>I-配置文件简化</strong><p>     我厌倦一切配置文件繁重的框架。 <br />     最好的情况是，<strong>框架提供极端灵活复杂的配置方式，但只在你需要的时候</strong>。<br /> <br />     Spring提供了三种可能来简化XML。随着国内用户水平的提高，这些基本的简化技巧大家都已掌握。<br />     大家可以直接看第3，第4点--Spring 1.2, Spring 2.0的后继改进。<br /></p><p><strong>1.1.autowire="byName" /"byType"</strong></p><p>     假设Controller有一个属性名为customerDAO，Spring就会在配置文件里查找有没有名字为CustomerDAO的bean, 自动为Controller注入。<br />     如果bean有两个属性，一个想默认注入，一个想自定义，只要设定了autowire，然后显式的声明那个想自定义的，就可以达到要求。这就应了需求，在需要特别配置的时候就提供配置，否则给我一个默认注入。<br /><br />     还有一个更懒的地方，在最最根部的&lt;beans&gt;节点写一句default-autovwrie="byName"，可以让文件里的所有bean 都默认autowrie。<br />    不过Rod认为开发期可以这样，但Production Server上不应该使用Autowire。而我觉得那些自定义一次的地方比如TranscationManager应该详细定义，而Dao,Controller这种大量重复定义的bean就可以偷点懒了。</p><p><strong>1.2.&lt;bean&gt;节点之间抽象公共定义和 Inner Bean</strong></p><p>    这太方便懒人了，想不到两个独立的XML节点都可以玩继承和派生，子节点拥有父节点的全部属性。<br />    最好用的地方就是那个Transtion Proxy的定义。先定义一个又长又冗的父类，然后用子类去继承它。<br />   <br />    另外，还有一个Inner Bean的机制，可以把DAO写成Proxy的内部类。为什么要写成内部类?为了让Proxy冒名顶替它去让Controller Autowire。(详见后面的示例) </p><p><strong>1.3. 宽松的配置, To XML or Not to XML <br /></strong>    据说Spring比Struts的配置宽松了很多，这就给人把东西从配置文件中撤回原码中的机会。<br />    不赞成什么都往配置文件里晒，造成了Rich Information的配置文件，修改或者查看的时候，要同时打开配置文件和原码才能清楚一切。 <br />    而我希望配置文件就集中做一些整体的配置，还有框架必须的、无需管理的冗余代码。而一些细节的变化不大的配置和逻辑，就尽量别往里塞了。因此，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 </span><span style="COLOR: #ff0000">name</span><span style="COLOR: #0000ff">="customerController"</span><span style="COLOR: #ff0000"> class</span><span style="COLOR: #0000ff">="org.springside.bookstore.web.CustomerController"</span><span style="COLOR: #ff0000"> 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 </span><span style="COLOR: #ff0000">id</span><span style="COLOR: #0000ff">="customerDAO"</span><span style="COLOR: #ff0000"> 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">  </span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">bean </span><span style="COLOR: #ff0000">id</span><span style="COLOR: #0000ff">="customerManager"</span><span style="COLOR: #ff0000"> parent</span><span style="COLOR: #0000ff">="baseTxService"</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br />        </span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">property </span><span style="COLOR: #ff0000">name</span><span style="COLOR: #0000ff">="target"</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br />            </span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">bean </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 />        </span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">property</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br />    </span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">bean</span><span style="COLOR: #0000ff">&gt;</span></div><p><b>3.Spring 1.2后xml语法简化<br /><br /> </b>最主要的简化是把属性值和引用bean从<strong>子节点</strong>变回了<strong>属性值</strong>，对不喜欢autowire的兄弟比较有用。<br /> 当然，如果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">  &lt;</span><span style="COLOR: #800000">property </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 alt="" src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />     </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">  &lt;/</span><span style="COLOR: #800000">property</span><span style="COLOR: #0000ff">&gt;<br /></span>  简化为<br /><span style="COLOR: #0000ff">  &lt;</span><span style="COLOR: #800000">property </span><span style="COLOR: #ff0000">name</span><span style="COLOR: #0000ff">="foo"</span><span style="COLOR: #ff0000"> value</span><span style="COLOR: #0000ff">="fooValue"</span><span style="COLOR: #0000ff">/&gt;</span><span style="COLOR: #000000"><img alt="" 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 alt="" src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">property </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 alt="" src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />   </span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">ref </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 alt="" 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 alt="" src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />简化为<br /><img alt="" 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: #ff0000">name</span><span style="COLOR: #0000ff">="foo"</span><span style="COLOR: #ff0000"> ref</span><span style="COLOR: #0000ff">="fooBean"</span><span style="COLOR: #0000ff">/&gt;</span></div></div><br /><br />3. list可以简化为空格分开的字符串</div><div>  
<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 alt="" src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">property </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 alt="" src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />  </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 alt="" src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />     </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">        <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 alt="" src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />  </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 alt="" 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 alt="" src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />简化为<br /><img alt="" 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: #ff0000">name</span><span style="COLOR: #0000ff">="myFriendList"</span><span style="COLOR: #ff0000"> value</span><span style="COLOR: #0000ff">="gigix wuyu"</span><span style="COLOR: #0000ff">/&gt;</span><span style="COLOR: #000000"><br /><img alt="" src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /></span></div></div>   <br />  <br /><strong>4.Spring 2.0来了</strong><br />   如果没什么外力刺激，spring xml 可能就这样不会变了。但现在xml成了过街老鼠，被ror的默认配置和JDK5的annotation逼得不行，当然就要继续求变。<br />   比如有好事者认为，节点名必须以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 </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 /> </span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">property </span><span style="COLOR: #ff0000">name</span><span style="COLOR: #0000ff">="maxCount"</span><span style="COLOR: #ff0000"> 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 </span><span style="COLOR: #ff0000">class</span><span style="COLOR: #0000ff">="org.springside....CustomerDAO"</span><span style="COLOR: #ff0000"> 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>    如果没有用Eclipse的Spring插件，那至少也要使用spring自带的dtd使XML编辑器smart一些，能够自动为你生成属性,判断节点/属性名称有没有拼错等。<br /><br /><strong>6.还有更变态的简化配置方法</strong><br />    比如autoproxy，不过我觉得更简化就不可控了，所以没有采用。<br /><br />因为Spring自带的sample离我们的实际项目很远，所以官方一点的model层模式展现就靠Appfuse了。<br />    但Appfuse的model层总共有一个DAO接口、一个DAOImpl类、一个Service接口、一个ServiceImpl类、一个DataObject.....大概只有受惯了虐待的人才会欣然接受吧。<br />    另外，Domain-Driven逢初一、十五也会被拿出来讨论一遍。<br /><br />    其实无论什么模式，都不过是一种人为的划分、抽象和封装。只要在团队里理解一致，自我感觉优雅就行了。<br />     我的建议是，一开始DO和Manager一生一旦包演全场，DO作为纯数据载体，而Manager类放置商业方法，用getHibernateTemplate()直接访问数据库，不强制基于接口编程。当某天系统复杂到你直觉上需要将DAO层和Service层分开时，再分开就好了。<br /><br />    <strong><font size="4">1.DataObject类<br /></font>     </strong>好听点也可以叫Domain Object。Domain Driven  Development虽然诱人，但因为Java下的ORM框架都是基于Data Mapper模式的，没有Ruby On Rails中那种Active Recorder的模式。所以，还是压下了这个欲望，Data Object纯粹作一个数据载体，而把数据库访问与商业逻辑操作统一放到Manager类中。<br /><br />    <strong><font size="4">2.Manager类</font></strong><br />    我的Manager类是Appfuse中DAO类与Service类的结合体，因为：<br /><br />    <strong>2.1 不想使用纯DAO</strong><br />     以往的DAO是为了透明不同数据库间的差异，而现在Hibernate已经做的很好。所以目前纯DAO的更大作用是为了将来可以切换到别的ORM方案比如iBatis，但一个Pragmaic的程序员显然不会无聊到为了这个机会不大的理由，现在就去做一个纯DAO层，项目又不是Appfuse那样为了demo各种ORM方案而存在。<br /><br />    <strong>2.2 也不使用纯的薄Service层<br /></strong>    在JPetStore里有一个很薄的Service层，Fascade了一堆DAO类，把这些DAO类的所有方法都僵硬的重复了一遍。而我认为Fascade的意义在二：<br />    一是Controller调用Manager甲的时候，总会伴随着调用Manager乙的某些方法。使用Fascade可以避免Controller零散的调用一堆Manager类。<br />    二是一个商业过程里可能需要同时调用DAO甲乙丙丁的方法。 <br /><br />     这些时候，Fascade都是合理的。但我讨厌类膨胀，所以我宁愿在甲乙丙丁中挑一个来充当Fascade的角色。有耦合的问题吗？对一个不是死搬书的Designer来说，组件边界之内的类之间的耦合并不是耦合。<br /><br />    <font size="4"><strong>3.去除不必要的基于接口编程</strong><br /></font>    众所周知，Spring是提倡基于接口编程的。<br />    但有些Manager类，比如SaleOrderManager ，只有5%的机会再有另一个Impl实现。95%时间里这两兄弟站一起，就像C++里的.h和.cpp，徒增维护的繁琐(经常要同步两个文件的函数声明)，和代码浏览跳转时的不便(比如从Controler类跟踪到Service类时，只能跳转到接口类的相应函数，还要再按一次复杂的热键才跳转到实现类)<br />    连Martin Flower都说，强制每个类都分离接口和实现是过犹不及。只在有多个独立实现，或者需要消除对实现类的依赖时，才需要分离接口。<br /><br />    <strong>3.1 DAO被强制用接口的原因</strong><br />    Spring IOC本身是不会强制基于接口的，但DAO类一般要使用Spring的声明式事务机制，而声明式的事务机制是使用Spring AOP来实现的。Spring AOP的实现机制包括动态代理和Cgilib2，其中Spring AOP默认使用的Java动态代理是必须基于接口，所以就要求基于接口了。<br />    <br />    <strong>3.2 解决方法</strong><br />    那就让Spring AOP改用CGLib2，生成目标类的子类吧，我们只要指定使用声明式事务的FactoryBean使用CGLib的方式来实现AOP，就可以不基于接口编程了。<br />    指定的方式为<strong>设置proxyTargetClass为true</strong>。如下：<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"><div><img alt="" src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">bean </span><span style="COLOR: #ff0000">class</span><span style="COLOR: #0000ff">="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"</span><span style="COLOR: #ff0000"><br /><img alt="" src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />id</span><span style="COLOR: #0000ff">="baseService"</span><span style="COLOR: #ff0000">   abstract</span><span style="COLOR: #0000ff">="true"</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br /><img alt="" 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: #ff0000">name</span><span style="COLOR: #0000ff">="transactionManager"</span><span style="COLOR: #ff0000"> ref</span><span style="COLOR: #0000ff">="transactionManager"</span><span style="COLOR: #0000ff">/&gt;</span><span style="COLOR: #000000"><br /><img alt="" 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: #ff0000">name</span><span style="COLOR: #0000ff">="proxyTargetClass"</span><span style="COLOR: #ff0000"> value</span><span style="COLOR: #0000ff">="true"</span><span style="COLOR: #0000ff">/&gt;</span><span style="COLOR: #000000"><br /><img alt="" src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /><img alt="" src="http://www.blogjava.net/images/dot.gif" /><br /><img alt="" 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><p><br />     又因为这些Service Bean都是单例，效率应该不受影响。<br /><br />    <strong><font size="4">4.总结<br /></font></strong>    对比Appfuse里面的5个类，我的Model层里只有VO作为纯数据载体，Manager类放商业方法。有人说这样太简单了，但一个应用，要划成几个JSP，一个Controller，一个Manager，一个VO，对我来说已经足够复杂，再要往上架墙叠屋，恕不奉陪，起码在我的项目范围里不需要。(但有很多项目是需要的，神佑世人）<br /><br />    后记：迫于世人的压力，<a href="http://www%20.springside.org.cn/">SpringSide</a>还是把DAO和Service层分开了，但依然坚持不搞那么多接口。<br /><br />Struts与Webwork的扇子请跳过本篇。<br /><br />    MVC不就是把M、V、C分开么？至唯物朴素的做法是两个JSP一个负责View，一个负责Controller，再加一个负责Model的Java Bean，已经可以工作得很好，那时候一切都很简单。<br />    而现在为了一些不是本质的功能，冒出这么多非标准的Web框架，实在让人一阵郁闷。像Ruby On Rails那样简捷开发，可用可不用，而且没有太多的限制需要学习的，比如<strong>Webwork</strong>这型还可以考虑。但像Struts那样越用框架越麻烦，或者像Tapestry那样有严重自闭倾向，额上凿着"高手专用玩具"的，用在团队里就是不负责任的行为了。<br /><br /></p><div>    so，<strong>我的MVC方案是使用Spring MVC的Controller接口，写最普通的JavaBean作为Controller</strong>，本质就和当年拿JSP作Controller差不多，但拥有了Spring IOC的特性。</div><div>    之所以用这么消极的选择标准，是因为觉得这一代MVC框架离重回RAD时代的标准还很远，注定了只是一段短暂的，过渡的技术，不值得投资太多精力和团队学习成本。</div><div><br /><strong>1. 原理</strong><div>     Spring MVC按植物分类学属于Martin Flower〈企业应用模式〉里的静态配置型Front Controler，使用DispatchServlet截获所有*.do的请求，按照xml文件的配置，调用对应的Command对象的handleRequest(request,response)函数，同时进行依赖对象的注入。<br />     我们的Controller层，就是实现handleRequest(request,response)函数的普通JavaBean。</div><div></div></div><div><strong><br />2. 优势<br />    </strong> Spring MVC与struts相比的优势:</div><div><strong>     </strong>一是它的Controller有着从松到紧的类层次结构，用户可以选择实现只有一个HandleRequest()函数的接口，也可以使用它有很多回调函数的SimpleFormController类。</div><div>     二是不需要Form Bean，也不需要Tapestry那所谓面向对象的页面对象，对于深怕类膨胀，改一个东西要动N个地方的人最适合不过。</div><div>     三是不需要强XML配置文件，宣告式编程是好的，但如果强制成框架，什么都要在xml里面宣告，写的时候繁琐，看的时候也要代码配置两边看才能明白就比较麻烦了。</div><div> </div><div>     那Webwork呢?没有实战过，不过因为对MVC框架所求就不多，单用Spring MVC的Controller已经可以满足需求，就不多搞一套Webwork来给团队设坎，还有给日后维护，spring,ww2之间的版本升级添麻烦了。真有什么需要添加的，Spring MVC源代码量很少，很容易掌控和扩展。<br />  </div><div><b>3.化简</b></div><div><strong>3.1. 直接implement Controller，实现handleRequest()函数</strong></div><div>      首先，simple form controller非我所好，一点都不simple。所以有时我会直接implement Controller接口。这个接口的唯一函数是供Front Controller调用的handleRequest(request,response)。<br />      如果需要application对象，比如想用application.getRealPath()时，就要extends webApplicationObjectSupport。<br /><br /><strong>3.2.每个Controler负责一组相关的action</strong></div><div>       我是坚决支持一个Controler负责多个action的，一个Controler一个action就像一个function一个类一样无聊。所以我用最传统的方式，用URL参数如msg="insert"把一组相关action交给一个Controler控制。ROR与制作中的Groovy On Rails都是这种模式，Spring也有MultiActionController支持。<br />       以上三者都是把URL参数直接反射为Controller的函数，而<a href="http://mc4j.org/confluence/display/stripes/Home"><font color="#002c99">Stripes</font></a>的设计可用annotation标注url action到响应函数的映射。</div><div>       
<div></div><strong>3.3.xml宣告式编程的取舍</strong> </div><div>    我的取舍很简单，反正Spring没有任何强制，我只在可能需要不重新编译而改变某些东西的时候，才把东西放在xml里动态注入。jsp路径之类的就统统收回到controller里面定义.</div><div> </div><div><strong>3.4.Data Binder</strong></div><div>       Data Binder是Controller的必有环节，对于Spring提供的DataBinder，照理完全可用，唯一不爽是对象如果有内嵌对象，如订单对象里面包含了Customer对象，Spring需要你先自行创建了Customer对象并把它赋给了Order对象，才可能实现order.customer.customer_no这样的绑定。我偷懒，又拿Jakarta BeanUtils出来自己做了一个Binder。<br /><br />3.<strong>5.提取基类</strong></div><div>      最后还是忍不住提取了一个基类，负责MultiAction和其他一些简便的方法。Sprnig的MultiActionController做得太死，规定所有函数的第1,2个参数必须是request和response，不懂动态的，温柔的进行参数注入。<br /><br />      <br />      经过化简再化简，已经是很简单一个Java Bean ，任谁都可以轻松上手，即使某年某月技术的大潮把现在所有MVC框架都淹没了，也不至于没人识得维护。<br />人生像个舞台，请良家少女离开。<br />    同样的，Freemarker和Velocity爱好者请跳过本篇。与弃用webwork而单用Spring MVC Controller接口的理由一样，<a href="http://www.freemarker.org/"><font color="#002c99">Freemarker</font></a>本来是一样好东西，还跨界支持jsp 的taglib，而且得到了WebWork的全力支持，但为了它的非标准化，用户数量与IDE的缺乏，在View层我们还是使用了<strong>保守但人人会用，IDE友好的JSP2.0 配合JSTL。<br /><br />    </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"> <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 </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 /> <strong>2. 最懒的form 数据绑定</strong></p><p>    Spring少得可怜的几个tag基本上是鸡肋，完全可以不要。 而Spring开发中的那些Simple Form tag又还没有发布。Spring的Tag主要用来把VO的值绑到input框上。但是，和Struts一样，需要逐个Input框绑定，而且语法极度冗长，遇到select框还要自己进行处理.....典型的Spring Sample页面让人一阵头晕. </p><p>    而<a href="http://jodd.sourceforge.net/doc/forms.html"><font color="#002c99">jodd的form tag</font></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 </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 </span><span style="COLOR: #ff0000">type</span><span style="COLOR: #0000ff">="text"</span><span style="COLOR: #ff0000"> 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 </span><span style="COLOR: #ff0000">type</span><span style="COLOR: #0000ff">="radiobox"</span><span style="COLOR: #ff0000"> name</span><span style="COLOR: #0000ff">="sex"</span><span style="COLOR: #ff0000"> 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 </span><span style="COLOR: #ff0000">name</span><span style="COLOR: #0000ff">="age"</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br />  </span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">option </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 />  </span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">option </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"> <br /></span></div></div><p><br /></p><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 alt="" src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">input </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"><font color="#002c99">修改后的源码可以在这里下载</font></a>。 </p><p><strong>3. DataGrid数据列表</strong></p><p>DisplayTag和ValueList都属于这种形式的Tag Library。但最近出现的<a href="http://www.extremecomponents.org/"><font color="#002c99">Extreme Table</font></a>是真正的killer，他本身功能强大不说，而且从一开始就想着如何让别人进行扩展重载，比如Extend Attributes机制就是DisplayTag这样的让千人一面者不会预留。<br /><br /><br /><strong>4.css, java script, ajax</strong><br />天下纷扰，没有什么特别想讲想推荐的，爱谁谁吧。<a href="http://www.amowa.net/buffalo/"><font color="#002c99">Buffalo</font></a>, DWR, Scriptaculous, Prototype, AjaxTags, AjaxAnywhere, Rico, Dojo, JSON-RPC，看着名字就头痛。<br /></p></div><img src ="http://www.blogjava.net/rendong/aggbug/61267.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/rendong/" target="_blank">rendong</a> 2006-08-02 00:57 <a href="http://www.blogjava.net/rendong/archive/2006/08/02/61267.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Hibernate/Spring/Struts架构使用OpenSessionInView的问题（http://forum.javaeye.com/viewtopic.php?t=15057）</title><link>http://www.blogjava.net/rendong/archive/2006/08/01/61202.html</link><dc:creator>rendong</dc:creator><author>rendong</author><pubDate>Tue, 01 Aug 2006 06:31:00 GMT</pubDate><guid>http://www.blogjava.net/rendong/archive/2006/08/01/61202.html</guid><wfw:comment>http://www.blogjava.net/rendong/comments/61202.html</wfw:comment><comments>http://www.blogjava.net/rendong/archive/2006/08/01/61202.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/rendong/comments/commentRss/61202.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/rendong/services/trackbacks/61202.html</trackback:ping><description><![CDATA[
		<span class="postbody">今天有一个朋友问了我一个问题，他使用的是Hibernate/Spring/Struts架构，配置使用Spring的OpenSessionInView Filter，但是发现不生效，lazy的集合属性在页面访问的时候仍然报session已经关闭的错误。我和他一起检查了所有的配置和相关的代码，但是没有发现任何问题。经过调试发现，应用程序使用的Session和OpenSessionInView Filter打开的Session不是同一个，所以OpenSessionInView模式没有生效，但是为什么他们不使用同一个Session呢？ <br />检查了一遍Spring的相关源代码，发现了问题的根源： <br />通常在Web应用中初始化Spring的配置，我们会在web.xml里面配置一个Listener，即： </span>    
<table cellspacing="1" cellpadding="3" width="90%" align="center" border="0"><tbody><tr><td><span class="genmed"><b>xml代码: </b></span></td></tr><tr><td class="code"><div style="FONT-FAMILY: 'Courier New', Courier, monospace"><span style="COLOR: #ddbb00">&lt;</span>listener<span style="COLOR: #ddbb00">&gt;</span> <br />   <span style="COLOR: #ddbb00">&lt;</span>listener-class<span style="COLOR: #ddbb00">&gt;<br /></span>     org.springframework.web.context.ContextLoaderListener<br /><span style="COLOR: #ddbb00">   &lt;</span>/listener-class<span style="COLOR: #ddbb00">&gt;</span> <br /><span style="COLOR: #ddbb00">&lt;</span>/listener<span style="COLOR: #ddbb00">&gt;</span></div></td></tr></tbody></table><span class="postbody">如果使用Struts，那么需要在Struts的配置文件struts-config.xml里面配置一个Spring的plugin：ContextLoaderPlugIn。 <br /><br />实际上ContextLoaderListener和ContextLoaderPlugIn的功能是重叠的，他们都是进行Spring配置的初始化工作的。因此，如果你不打算使用OpenSessionInView，那么你并不需要在web.xml里面配置ContextLoaderListener。 <br /><br />好了，但是你现在既需要Struts集成Spring，又需要OpenSessionInView模式，问题就来了！ <br /><br />由于ContextLoaderListener和ContextLoaderPlugIn功能重叠，都是初始化Spring，你不应该进行两次初始化，所以你不应该同时使用这两者，只能选择一个，因为你现在需要集成Struts，所以你只能使用ContextLoaderPlugIn。 <br /><br />但是令人困惑的是，ContextLoaderListener和ContextLoaderPlugIn有一个非常矛盾的地方！ <br /><br />ContextLoaderListener初始化spring配置，然后把它放在ServletContext对象里面保存： <br /><br /></span><table cellspacing="1" cellpadding="3" width="90%" align="center" border="0"><tbody><tr><td><span class="genmed"><b>java代码: </b></span></td></tr><tr><td class="code"><div style="FONT-FAMILY: 'Courier New', Courier, monospace"><br />servletContext.<span style="COLOR: #000000">setAttribute</span><span style="COLOR: #000000">(</span><br />                                        WebApplicationContext.<span style="COLOR: #000000">ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE</span>, this.<span style="COLOR: #000000">context</span><span style="COLOR: #000000">)</span>;</div></td></tr></tbody></table><span class="postbody"><br />请注意，保存的对象的key是WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE！<br />但是ContextLoaderPlugIn初始化spring配置，然后把它放在ServletContext对象里面保存： <br /><br /></span><table cellspacing="1" cellpadding="3" width="90%" align="center" border="0"><tbody><tr><td><span class="genmed"><b>java代码: </b></span></td></tr><tr><td class="code"><div style="FONT-FAMILY: 'Courier New', Courier, monospace"><br /><br /><span style="COLOR: #aaaadd" ?="">String</span> attrName = getServletContextAttributeName<span style="COLOR: #000000">(</span><span style="COLOR: #000000">)</span>; <br />getServletContext<span style="COLOR: #000000">(</span><span style="COLOR: #000000">)</span>.<span style="COLOR: #000000">setAttribute</span><span style="COLOR: #000000">(</span>attrName, wac<span style="COLOR: #000000">)</span>;</div></td></tr></tbody></table><span class="postbody"><br />这个attrName和WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE名字是不一样的！ <br /><br />如果仅仅是名字不一样，问题还不大，你仍然可以放心使用ContextLoaderPlugIn，但是当你使用OpenSessionInView的时候，OpenSessionInViewFilter是使用哪个key取得spring配置的呢？ <br /><br /></span><table cellspacing="1" cellpadding="3" width="90%" align="center" border="0"><tbody><tr><td><span class="genmed"><b>java代码: </b></span></td></tr><tr><td class="code"><div style="FONT-FAMILY: 'Courier New', Courier, monospace"><br />WebApplicationContext wac = <br />                                WebApplicationContextUtils.<span style="COLOR: #000000">getRequiredWebApplicationContext</span><span style="COLOR: #000000">(</span>getServletContext<span style="COLOR: #000000">(</span><span style="COLOR: #000000">)</span><span style="COLOR: #000000">)</span>;</div></td></tr></tbody></table><span class="postbody"><br /><br />显然，OpenSessionInViewFilter是按照WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE这个key去拿spring配置的！ <br /><br />我们整理一下思路： <br /><br />ContextLoaderPlugIn保存spring配置的名字叫做attrName； <br />，ContextLoaderListener保存spring配置的名字叫做WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE； <br />而OpenSessionInView是按照WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE这个名字去取得spring配置的！ <br />而你的应用程序却是按照attrName去取得spring的配置的！ <br /><br />所以，OpenSessionInView模式失效！ <br /><br />解决办法： <br />修改ContextLoaderPlugIn代码，在getServletContext().setAttribute(attrName, wac);这个地方加上一行代码： <br />getServletContext().setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, wac); <br /><br />或者修改OpenSessionInViewFilter，让它按照attrName去取得spring配置。<br /><br /><br /><br /><span class="postbody">我原来用struts/spring/hibernate的时候同样使用OpenSessionInView，但是似乎没有robbin所说的问题啊。而且我在使用的时候，是ContextLoaderListener和ContextLoaderPlugIn一起用的。整个配置如下： <br />1.首先是web.xml <br /></span><table cellspacing="1" cellpadding="3" width="90%" align="center" border="0"><tbody><tr><td><span class="genmed"><b>java代码: </b></span></td></tr><tr><td class="code"><div style="FONT-FAMILY: 'Courier New', Courier, monospace"><br /><br />        &lt;filter&gt; <br />        &lt;filter-name&gt;OpenSessionInViewFilter&lt;/filter-name&gt; <br />        &lt;filter-<span style="FONT-WEIGHT: bold; COLOR: #990066" ?="">class</span>&gt;org.<span style="COLOR: #000000">springframework</span>.<span style="COLOR: #000000">orm</span>.<span style="COLOR: #000000">hibernate</span>.<span style="COLOR: #000000">support</span>.<span style="COLOR: #000000">OpenSessionInViewFilter</span>&lt;/filter-<span style="FONT-WEIGHT: bold; COLOR: #990066" ?="">class</span>&gt; <br />    &lt;/filter&gt; <br />    <br />    &lt;filter-mapping&gt; <br />        &lt;filter-name&gt;OpenSessionInViewFilter&lt;/filter-name&gt; <br />        &lt;url-pattern&gt;<span style="COLOR: #6666ff">/*&lt;/url-pattern&gt; <br />    &lt;/filter-mapping&gt; <br />    <br />    &lt;listener&gt; <br />                &lt;listener-class&gt;org.springframework.web.context.ContextLoaderListener&lt;/listener-class&gt; <br />        &lt;/listener&gt; <br /><br />...... <br /></span></div><br /></td></tr></tbody></table><span class="postbody"><br /><br />2. 然后是struts-config.xml： <br /></span><table cellspacing="1" cellpadding="3" width="90%" align="center" border="0"><tbody><tr><td><span class="genmed"><b>java代码: </b></span></td></tr><tr><td class="code"><div style="FONT-FAMILY: 'Courier New', Courier, monospace"><br /><br />&lt;plug-in className="org.<span style="COLOR: #000000">springframework</span>.<span style="COLOR: #000000">web</span>.<span style="COLOR: #000000">struts</span>.<span style="COLOR: #000000">ContextLoaderPlugIn</span>"&gt; <br />        &lt;set-property property="contextConfigLocation" <br />                                  value="/WEB-INF/action-servlet.<span style="COLOR: #000000">xml</span>" <br />        /&gt; <br />&lt;/plug-in&gt; <br /></div><br /></td></tr></tbody></table><span class="postbody"><br /><br />其余部分省略。 <br /><br />在上述配置下，使用OpenSessionInView似乎没有问题。 <br /><br />不知道robbin所说的ContextLoaderListener和ContextLoaderPlugIn不应该同时使用是不是做得是如下的配置：（struts-config.xml） <br /><br /></span><table cellspacing="1" cellpadding="3" width="90%" align="center" border="0"><tbody><tr><td><span class="genmed"><b>java代码: </b></span></td></tr><tr><td class="code"><div style="FONT-FAMILY: 'Courier New', Courier, monospace"><br /><br />&lt;plug-in <br />className="org.<span style="COLOR: #000000">springframework</span>.<span style="COLOR: #000000">web</span>.<span style="COLOR: #000000">struts</span>.<span style="COLOR: #000000">ContextLoaderPlugIn</span>"&gt; <br />&lt;set-property property="contextConfigLocation" <br />value="/WEB-INF/applicationContext.<span style="COLOR: #000000">xml</span>, <br />/WEB-INF/action-servlet.<span style="COLOR: #000000">xml</span>"/&gt; <br />&lt;/plug-in&gt; <br /></div><br /></td></tr></tbody></table><span class="postbody"><br /><br />我尝试了一下，用这种配置时，OpenSessionInView的确失效了。 <br /><br />我猜想，原因大概是这样：struts的这个plugIn，可能只是为了整合一个action-servlet.xml，将action-servlet.xml中的定义当作Spring的bean来使用，因此，在保存时，只要有action-servlet.xml的配置，就被保存到robbin所提到的那个attrName中，而不是WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE中，所以，OpenSessionInView是取不到这个配置的。 <br /><br />那么这个配置什么时候被取到呢？直觉告诉我，可能是和Action的Proxy有关。于是，查看了org.springframework.web.struts.DelegatingActionProxy的源码，果然： <br /></span><table cellspacing="1" cellpadding="3" width="90%" align="center" border="0"><tbody><tr><td><span class="genmed"><b>java代码: </b></span></td></tr><tr><td class="code"><div style="FONT-FAMILY: 'Courier New', Courier, monospace"><br /><br /><span style="COLOR: #6666ff">/** <br />        * Return the delegate Action for the given mapping. <br />        * &lt;p&gt;The default implementation determines a bean name from the <br />        * given ActionMapping and looks up the corresponding bean in the <br />        * WebApplicationContext. <br />        * @param mapping the Struts ActionMapping <br />        * @return the delegate Action <br />        * @throws BeansException if thrown by WebApplicationContext methods <br />        * @see #determineActionBeanName <br />        */</span><br />        <span style="FONT-WEIGHT: bold; COLOR: #990066" ?="">protected</span><span style="COLOR: #aaaadd" ?="">Action</span> getDelegateAction<span style="COLOR: #000000">(</span>ActionMapping mapping<span style="COLOR: #000000">)</span><span style="FONT-WEIGHT: bold; COLOR: #990066" ?="">throws</span> BeansException <span style="COLOR: #000000">{</span><br />                WebApplicationContext wac = getWebApplicationContext<span style="COLOR: #000000">(</span>getServlet<span style="COLOR: #000000">(</span><span style="COLOR: #000000">)</span>, mapping.<span style="COLOR: #000000">getModuleConfig</span><span style="COLOR: #000000">(</span><span style="COLOR: #000000">)</span><span style="COLOR: #000000">)</span>; <br />                <span style="COLOR: #aaaadd" ?="">String</span> beanName = determineActionBeanName<span style="COLOR: #000000">(</span>mapping<span style="COLOR: #000000">)</span>; <br />                <span style="FONT-WEIGHT: bold; COLOR: #990066" ?="">return</span><span style="COLOR: #000000">(</span><span style="COLOR: #aaaadd" ?="">Action</span><span style="COLOR: #000000">)</span> wac.<span style="COLOR: #000000">getBean</span><span style="COLOR: #000000">(</span>beanName, <span style="COLOR: #aaaadd" ?="">Action</span>.<span style="COLOR: #000000">class</span><span style="COLOR: #000000">)</span>; <br />        <span style="COLOR: #000000">}</span><br /><br />        <span style="COLOR: #6666ff">/** <br />        * Fetch ContextLoaderPlugIn's WebApplicationContext from the <br />        * ServletContext, containing the Struts Action beans to delegate to. <br />        * @param actionServlet the associated ActionServlet <br />        * @param moduleConfig the associated ModuleConfig <br />        * @return the WebApplicationContext <br />        * @throws IllegalStateException if no WebApplicationContext could be found <br />        * @see DelegatingActionUtils#getRequiredWebApplicationContext <br />        * @see ContextLoaderPlugIn#SERVLET_CONTEXT_PREFIX <br />        */</span><br />        <span style="FONT-WEIGHT: bold; COLOR: #990066" ?="">protected</span> WebApplicationContext getWebApplicationContext<span style="COLOR: #000000">(</span><br />                        ActionServlet actionServlet, ModuleConfig moduleConfig<span style="COLOR: #000000">)</span><span style="FONT-WEIGHT: bold; COLOR: #990066" ?="">throws</span><span style="COLOR: #aaaadd" ?="">IllegalStateException</span><span style="COLOR: #000000">{</span><br />                <span style="FONT-WEIGHT: bold; COLOR: #990066" ?="">return</span> DelegatingActionUtils.<span style="COLOR: #000000">getRequiredWebApplicationContext</span><span style="COLOR: #000000">(</span>actionServlet, moduleConfig<span style="COLOR: #000000">)</span>; <br />        <span style="COLOR: #000000">}</span><br /></div><br /></td></tr></tbody></table><span class="postbody"><br /><br />仔细看其中的取wac的代码，它并不是从WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE取的wac。 <br /><br />由此，我相信，除了robbin讲的修改源码以外，同时使用ContextLoaderListener和ContextLoaderPlugIn，但是不要在ContextLoaderPlugIn里面加入applicationContext.xml，只要加入你的action-servlet.xml，我相信，同样也可以非常流畅的使用OpenSessionInView</span> 。<br /><br /><br /><span class="postbody">我也遇到了上面说的openSessionInView不起作用的问题（web.xml既定义了listener，也定义了struts plugin），我想问一下，上面提到的action-servlet.xml到底是什么内容？ <br />在我的应用里spring的配置文件是application-context.xml，它本身是空的，引用spring-data.xml，sping-security.xml等等和存放对应struts action的spring 配置文件spring-struts-action.xml。 <br />struts的配置文件是struts-config.xml，里面定义了所有的action，它们的class都是org.springframework.web.struts.DelegatingActionProxy。最后的plug-in是 <br /></span><table cellspacing="1" cellpadding="3" width="90%" align="center" border="0"><tbody><tr><td><span class="genmed"><b>java代码: </b></span></td></tr><tr><td class="code"><div style="FONT-FAMILY: 'Courier New', Courier, monospace"><br /><br />&lt;plug-in <br />className="org.<span style="COLOR: #000000">springframework</span>.<span style="COLOR: #000000">web</span>.<span style="COLOR: #000000">struts</span>.<span style="COLOR: #000000">ContextLoaderPlugIn</span>"&gt; <br />&lt;set-property property="contextConfigLocation" <br />value="/WEB-INF/applicationContext.<span style="COLOR: #000000">xml</span>"/&gt; <br />&lt;/plug-in&gt; <br /></div><br /></td></tr></tbody></table><span class="postbody"><br /><br />结果也遇到了openSessionInView不起作用的问题 <br />在我的应用里都没有出现过action-servlet.xml，我想问下它到底是什么？是对应于我的spring-struts-action.xml还是struts-config.xml引用的一部分？ <br /><br />通俗的说，这个action-servlet.xml到底是spring配置文件还是struts的配置文件？</span><br /><br /><br /><br /><br /><br /><br /><span class="postbody">我仔细想了一下，那个action-servlet.xml应该是spring配置的一部分，也就是说对应我的spring-struts-action.xml（明确的说，这个里面的xml语法是spring配置文件的），应该是这样的吧？不过按照这个理解下去，我又产生了问题。 <br />我的理解时这样的，spring里面的listener会在web.xml里加载spring的容器，struts ActionServlet初始化时又会根据struts-config.xml里的spring plugin配置再初始化一个spring容器，所以原则上说只要一个就可以了，如果2处都配了，会初始化2个spring容器，在和struts结合的用法里，实际有效的是stuts配置里面那个plugin初始化的容器，因为用户操作的入口都是struts的action。那么二楼提供的方法其实就是所有的bean都由那个listener初始化的，存在于第一个spring容器中，然后stuts只初始化那些和struts action关联的action bean，存在第二个容器里（这两个容器的区分就在于robbin提到的他们的名字不同）但是问题就是： <br />为什么在二楼的的方法中，用户通过action访问spring bean，那么应该只是访问的第二个容器里的action bean，而service bean在第一个容器里，那第二个容器里的action bean是怎么会可以访问到第一个容器里的service bean和其他所有spring bean的呢？实在是费解</span> 。<br /><br /><br /><br /><br /><br /><br /><br /><span class="postbody">感谢搂主的分析，spring的struts plugin确实有上述描述的问题 <br />如果根据原来的方法，context会初始化2次，看了plugin的源码以后我对它作了小小的修改，首先检查context是不是被初始化过，如果有则直接从attribute中获取，如果没有初始化，则根据plugin的配置初始化，同时保证了context只被初始化一次。 <br />原来的意图是屏蔽web.xml中的context监听，直接用plugin初始化context，但启动失败，于是作了上述修改 <br /></span><table cellspacing="1" cellpadding="3" width="90%" align="center" border="0"><tbody><tr><td><span class="genmed"><b>java代码: </b></span></td></tr><tr><td class="code"><div style="FONT-FAMILY: 'Courier New', Courier, monospace"><br /><br /><span style="COLOR: #6666ff">//read the application context from the aplication attribute</span><br />                WebApplicationContext wac = <span style="FONT-WEIGHT: bold; COLOR: #990066" ?="">null</span>; <br />                <span style="COLOR: #aaaadd" ?="">String</span> attrName = getServletContextAttributeName<span style="COLOR: #000000">(</span><span style="COLOR: #000000">)</span>; <br />                <span style="FONT-WEIGHT: bold; COLOR: #990066" ?="">if</span><span style="COLOR: #000000">(</span>getServletContext<span style="COLOR: #000000">(</span><span style="COLOR: #000000">)</span>.<span style="COLOR: #000000">getAttribute</span><span style="COLOR: #000000">(</span>WebApplicationContext.<span style="COLOR: #000000">ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE</span><span style="COLOR: #000000">)</span>!=<span style="FONT-WEIGHT: bold; COLOR: #990066" ?="">null</span><span style="COLOR: #000000">)</span><span style="COLOR: #000000">{</span>                        <br />                        wac = <span style="COLOR: #000000">(</span>WebApplicationContext<span style="COLOR: #000000">)</span> getServletContext<span style="COLOR: #000000">(</span><span style="COLOR: #000000">)</span>.<span style="COLOR: #000000">getAttribute</span><span style="COLOR: #000000">(</span>WebApplicationContext.<span style="COLOR: #000000">ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE</span><span style="COLOR: #000000">)</span>; <br />                        logger.<span style="COLOR: #000000">info</span><span style="COLOR: #000000">(</span>"Using the context listener<span style="COLOR: #0000ff">'s context "+wac); <br />                } <br />                else { <br />                        logger.info("Load the context plugin application context "); <br />                        WebApplicationContext parent = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); <br /><br />                        wac = createWebApplicationContext(parent); <br />                        if (logger.isInfoEnabled()) { <br />                                logger.info("Using context class '</span>" + wac.<span style="COLOR: #000000">getClass</span><span style="COLOR: #000000">(</span><span style="COLOR: #000000">)</span>.<span style="COLOR: #000000">getName</span><span style="COLOR: #000000">(</span><span style="COLOR: #000000">)</span> + "<span style="COLOR: #0000ff">' for servlet '</span>" + getServletName<span style="COLOR: #000000">(</span><span style="COLOR: #000000">)</span> + "<span style="COLOR: #0000ff">'"); <br />                        }                        <br />                        //set to attribute to spring listener <br />                        getServletContext().setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, wac);                 <br />                } <br />                <br />                // Publish the context as a servlet context attribute. <br />                getServletContext().setAttribute(attrName, wac); <br /></span></div><br /></td></tr></tbody></table></span><span class="postbody"><br /><br />PS. 有个疑问，如果说spirng中的bean只有一个实例，应该说无论初始化多少次都应该获得的是同一个实例啊？</span><style type="text/css"><!--
td.attachrow		{ font: normal 11px Verdana, Arial, Helvetica, sans-serif; color : #000000; border-color : #000000; }
td.attachheader     { font: normal 11px Verdana, Arial, Helvetica, sans-serif; color : #000000; border-color : #000000; background-color: #D1D7DC; }
table.attachtable	{ font: normal 12px Verdana, Arial, Helvetica, sans-serif; color : #000000; border-color : #000000;	border-collapse : collapse; }
--></style><br /><br /><div align="center"><hr width="95%" /></div><table class="attachtable" cellspacing="0" cellpadding="2" width="95%" align="center" border="1"><tbody><tr><td class="attachheader" align="middle" width="100%" colspan="3"><b><span class="gen">strutspugin.rar</span></b></td></tr><tr><td class="attachrow" width="15%"><span class="genmed"> 描述:</span></td><td class="attachrow" width="75%"><table cellspacing="4" cellpadding="0" width="100%" align="center" border="0"><tbody><tr><td class="attachrow"><span class="genmed">根据spirng 1.2.7 重新编译的ContextLoaderPlugin</span></td></tr></tbody></table></td><td class="attachrow" align="middle" width="10%" rowspan="4"><img alt="" src="http://forum.javaeye.com/images/icon_clip.gif" border="0" /><br /><a class="genmed" href="http://forum.javaeye.com/download.php?id=1729&amp;sid=80313ea024cd61caf955261ac1ef0dc4"><b><font color="#002c99">下载</font></b></a></td></tr><tr><td class="attachrow" width="15%"><span class="genmed"> 文件名:</span></td><td class="attachrow" width="75%"><span class="genmed"> strutspugin.rar</span></td></tr><tr><td class="attachrow" width="15%"><span class="genmed"> 文件大小:</span></td><td class="attachrow" width="75%"><span class="genmed"> 4.16 KB</span></td></tr><tr><td class="attachrow" width="15%"><span class="genmed"> 下载过的:</span></td><td class="attachrow" width="75%"><span class="genmed"> 文件被下载或查看 194 次</span></td></tr></tbody></table><div align="center"><hr width="95%" /></div><style type="text/css"><!--
td.attachrow		{ font: normal 11px Verdana, Arial, Helvetica, sans-serif; color : #000000; border-color : #000000; }
td.attachheader     { font: normal 11px Verdana, Arial, Helvetica, sans-serif; color : #000000; border-color : #000000; background-color: #D1D7DC; }
table.attachtable	{ font: normal 12px Verdana, Arial, Helvetica, sans-serif; color : #000000; border-color : #000000;	border-collapse : collapse; }
--></style><style type="text/css"><!--
td.attachrow		{ font: normal 11px Verdana, Arial, Helvetica, sans-serif; color : #000000; border-color : #000000; }
td.attachheader     { font: normal 11px Verdana, Arial, Helvetica, sans-serif; color : #000000; border-color : #000000; background-color: #D1D7DC; }
table.attachtable	{ font: normal 12px Verdana, Arial, Helvetica, sans-serif; color : #000000; border-color : #000000;	border-collapse : collapse; }
--></style><br /><br /><br /><br /><br /><span class="postbody">我觉得这个根本就是大家对Spring的理解的问题。 <br /><br />如果这真是一个严重的问题，以至于需要修改源码来修正，Spring的team不会到现在没有发现，到现在还没有修正。为什么Spring的context分成了多个文件？为什么用applicationContext.xml了，还有xxx-servlet.xml？ <br /><br /><br />如果大家监听ContextRefreshedEvent的话，会发现一个web app至少会有两个这样的event，下面是我的现在的应用打印出的context及其所包含的beans： <br /><span style="COLOR: darkblue">org.springframework.web.context.support.XmlWebApplicationContext: display name [Root WebApplicationContext]; startup date <br />[Wed May 10 17:30:13 CST 2006]; child of [org.springframework.context.support.ClassPathXmlApplicationContext: display name [org.springframework.context.support.ClassPathXmlApplicationContext;hashCode=3736840]; startup date [Wed May 10 17:30:09 CST <br />2006]; root of context hierarchy]; config locations <br />[/WEB-INF/webApplicationContext.xml,/WEB-INF/webApplicationContext-*.xml,/WEB-INF/standardJspApplicationContext.xml,classpath <br />*:config/spring/app-context-base.xml,classpath*:config/spring/app-context-hibernate.xml,classpath*:config/spring/app-context- <br />ibatis.xml,classpath*:config/spring/app-context-integration.xml,classpath*:config/spring/app-context-biz.xml] <br /></span><br /><span style="COLOR: blue">[messageSource, localeResolver, exposeSpringBeanDefinition, dataListOfTerminalInfoForm, dataListOfPointsSpecialOfferForm, <br />dataListOfSearchTerminalForm, pointsSpecialOfferForm1, terminalInfoForm1, searchTerminalForm1, <br />dataListOfSearchCardAccountDetailForm, dataListOfSearchPhysicalCardInfoForm, dataListOfSearchCardApplicationInfoForm, <br />dataListOfSearchTransactionForm, searchCardAccountDetailForm1, searchCardAccountDetailForm2, searchCardAccountDetailForm3, <br />searchPhysicalCardInfoForm1, searchPhysicalCardInfoForm2, searchPhysicalCardInfoForm3, searchTransactionForm1, searchTransactionForm2, searchTransactionForm3, displayTransactionFormForTest, dataListOfReplaceCardForm, dataListOfSearchCardInfoForm, cardInfoFormForTest, cardAccountForm1, cardAccountForm2, cardAccountForm3, searchCardInfoForm1, searchCardInfoForm2, searchCardInfoForm3, replaceCardForm1, replaceCardForm2, replaceCardForm3, <br />searchCardApplicationInfoForm1, searchCardApplicationInfoForm2, searchCardApplicationInfoForm3, <br />displayCardApplicationInfoFormForTest, displayCardApplicationInfoFormForTest1, csvDisplayProvider, excelDisplayProvider, <br />classicLook, simpleLook, microsoftLook, dacHandler, integer0, integer1, integer2, integer3, integer4, integer5, integer6, integer7, integer8, integer9, sessionFactory, transactionManager, hibernateTemplate, abstractHibernateDao, abstractDacHolderHibernateDao, ageLevelDefinitionDao, auditLogDao, bankDao, bankBranchDao, binRangeDao, cardDao, <br />cardAccountDao, cardAccountDetailDao, cardApplicationDao, cardSalesAgentDao, cardTypeDefinitionDao, centerDao, <br />centerAccountDao, centerAccountDetailDao, corporationDao, corporationTypeDefinitionDao, csaAccountDao, csaAccountDetailDao, csaBillsDao, csaTypeDefinitionDao, educationLevelDefinitionDao, generalLedgerDao, generalLedgerDetailDao, generalLedgerTypeDefinitionDao, identificationTypeDefinitionDao, incomeLevelDefinitionDao, industryDao, journalDao, journalBackupDao, maritalStatusDefinitionDao, merchantDao, merchantAccountDao, merchantAccountDetailDao, merchantApplicationDao, merchantBillsDao, merchantTypeDefinitionDao, occupationTypeDefinitionDao, outboundEmailDao, permissionDao, physicalCardDao, pointsSpecialOfferDao, residentialTypeDefinitionDao, roleDefinitionDao, rolePermissionDao, <br />systemConfigDao, terminalDao, terminalConfigurationDao, terminalModelDefinitionDao, transactionSummaryDao, <br />transactionTypeDefinitionDao, userDao, userCreditRatingDao, userLevelDefinitionDao, userRoleDao, sqlMapClient, <br />abstractIbatisValueListAdapter, valueListHandler, propertyConfigurer, dataSource, voidTransactionTemplate, <br />inquiryBalanceTransactionTemplate, definitionBizFacade, facadeHolder, pointsTransactionTemplate, emailBizObject, <br />clsSpringEventListener, balanceBizFacadeTarget, kernelBizObject, printBizFacadeTarget, pointsSpecialOfferBizFacadeTarget, <br />settlementBizObject, addPointsTransactionTemplate, inquiryMerchantAccountInfoTransactionTemplate, <br />addMerchantPointsTransactionTemplate, emailBizFacadeTarget, centerBizFacadeTarget, monitorBizFacadeTarget, <br />endOfDayReportTransactionTemplate, cardBizFacadeTarget, pointsCalculator, balanceBizObject, pointsSpecialOfferBizObject, <br />auditLogBizFacadeTarget, terminalBizFacadeTarget, terminalBizObject, templateHolder, settlementBizFacadeTarget, <br />merchantBizObject, userBizObject, changePinTransactionTemplate, centerPurchasePointsBackTransactionTemplate, <br />definitionBizObject, monitorBizObject, auditLogBizObject, merchantBizFacadeTarget, userBizFacadeTarget, <br />responseMessageDataFactoryBean, tradingBizObject, printBizObject, csaBizObject, csaBizFacadeTarget, kernelBizFacadeTarget, <br />cardBizObject, centerBizObject, tradingBizFacadeTarget, downloadParametersTransactionTemplate, baseTransactionProxy, <br />abstractDataFacade, balanceBizFacade, printBizFacade, settlementBizFacade, emailBizFacade, kernelBizFacade, <br />auditLogBizFacade, pointsSpecialOfferBizFacade, terminalBizFacade, userBizFacade, merchantBizFacade, csaBizFacade, <br />monitorBizFacade, cardBizFacade, centerBizFacade, tradingBizFacade, pointsConverter, transactionTypeHelperBean, integer100, integer106, integer110, integer111, integer120, integer180, integer181, integer182, integer200, integer220, integer221]</span><br /><br /><span style="COLOR: darkblue"><br />---------------------------------------------------------------context2 <br />org.springframework.web.context.support.XmlWebApplicationContext: display name [WebApplicationContext for namespace <br />'action-servlet']; startup date [Wed May 10 17:31:01 CST 2006]; child of <br />[org.springframework.web.context.support.XmlWebApplicationContext: display name [Root WebApplicationContext]; startup date <br />[Wed May 10 17:30:13 CST 2006]; child of [org.springframework.context.support.ClassPathXmlApplicationContext: display name <br />[org.springframework.context.support.ClassPathXmlApplicationContext;hashCode=3736840]; startup date [Wed May 10 17:30:09 CST <br />2006]; root of context hierarchy]; config locations <br />[/WEB-INF/webApplicationContext.xml,/WEB-INF/webApplicationContext-*.xml,/WEB-INF/standardJspApplicationContext.xml,classpath <br />*:config/spring/app-context-base.xml,classpath*:config/spring/app-context-hibernate.xml,classpath*:config/spring/app-context- <br />ibatis.xml,classpath*:config/spring/app-context-integration.xml,classpath*:config/spring/app-context-biz.xml]]; config <br />locations [/WEB-INF/action-servlet.xml] <br /></span><br /><span style="COLOR: blue">[/EditCurrentUserInfoAction, /FakeLoginAction, /SaveCardInfoAction, /LoginAction, /EditOperatorPswAction, <br />/SavePointsSpecialOfferAction, /DisplayCurrentUserInfoAction, /DisplayOperatorApplicationAction, /EditEmailAction, <br />/RegisterCardAction, /IssueCardsAction, /SearchEmailAction, /RegisterCsaAction, /EditTerminalInfoAction, <br />/ReleaseTerminalAction, /ReleaseCsaAction, /SaveTerminalInfoAction, /SearchTransactionAction, /SearchOperatorInfoAction, <br />/ProcessCardApplicationAction, /EditTerminalConfigurationAction, /EditGenericUserByIdAction, /SearchMerchantInfoAction, <br />/SearchTerminalAction, /SaveCsaPswAction, /SaveCsaInfoAction, /SaveCardTypeAction, /RegisterPointsSpecialOfferAction, <br />/SearchCardInfoAction, /EditMerchantInfoAction, /SearchCardAccountDetailAction, /SearchCardApplicationInfoAction, <br />/DisplayTransactionStatisticsAction, /DisplayRegisterCardInfoAction, /SaveEmailAction, /EditCardInfoByIdAction, <br />/MoniterSystemLogAction, /ReleasePointsSpecialOfferAction, /SearchMerchantAccountDetailAction, /EditMerchantPswAction, <br />/ReleaseMerchantAction, /ListCardTypeAction, /StockCardsAction, /ProcessOperatorApplicationAction, <br />/SearchPhysicalCardInfoAction, /SearchCsaInfoAction, /SearchOperatorApplicationAction, /ReleaseOperatorAction, <br />/DisplayCardApplicationInfoAction, searchEmailValueListBuilder, /SearchCsaApplicationAction, /RegisterCardTypeAction, <br />/MonitorTerminalStatusAction, /SearchCsaAccountDetailAction, /SearchUserAction, /ReleaseCardTypeAction, /ReleaseUserAction, <br />/ReleaseEmailAction, /CreateBlankCardsAction, /RegisterBulkCardsAction, /SaveMerchantPswAction, <br />/SearchPointsSpecialOfferAction, /EditPswAction, /SearchMerchantApplicationAction, /DisplayCsaApplicationAction, <br />/EndOfDayAction, /EditPointsSpecialOfferAction, /DisplayMerchantApplicationAction, /RegisterEmailAction, /EditCsaPswAction, <br />/ProcessMerchantApplicationAction, /EditCardTypeDefinitionAction, /SaveOperatorInfoAction, /SaveMerchantInfoAction, <br />/SaveOperatorPswAction, /EditOperatorInfoAction, /RegisterMerchantAction, /EditCsaInfoAction, /ChangeSystemStatusAction, <br />/ProcessSystemLogAction, /RegisterOperatorAction, /RegisterTerminalAction, /DisplayTransanctionAction, <br />/ProcessCsaApplicationAction, /MerchantPointsRedeemTransactionReportAction, /SaveTerminalConfigurationAction, <br />autoProxyCreator, profilingAdvice, profilingAdvisor, strutsActionAdvice, baseSearchAction, userTypeBasedSelector, <br />valueListBuilder]</span><br /><br /><span style="COLOR: darkblue"><br />------------------------------------------------------------------------context3 <br />org.springframework.web.context.support.XmlWebApplicationContext: display name [WebApplicationContext for namespace <br />'trading-servlet']; startup date [Wed May 10 17:31:08 CST 2006]; child of <br />[org.springframework.web.context.support.XmlWebApplicationContext: display name [Root WebApplicationContext]; startup date <br />[Wed May 10 17:30:13 CST 2006]; child of [org.springframework.context.support.ClassPathXmlApplicationContext: display name <br />[org.springframework.context.support.ClassPathXmlApplicationContext;hashCode=3736840]; startup date [Wed May 10 17:30:09 CST <br />2006]; root of context hierarchy]; config locations <br />[/WEB-INF/webApplicationContext.xml,/WEB-INF/webApplicationContext-*.xml,/WEB-INF/standardJspApplicationContext.xml,classpath <br />*:config/spring/app-context-base.xml,classpath*:config/spring/app-context-hibernate.xml,classpath*:config/spring/app-context- <br />ibatis.xml,classpath*:config/spring/app-context-integration.xml,classpath*:config/spring/app-context-biz.xml]]; config locations [/WEB-INF/trading-servlet.xml] <br /></span><span style="COLOR: blue">[clsRawTagElementParser, transactionProcessor, clsTradingServlet, rawTagElementParser]</span><br /><br /><br /><br />如果照大家所说的方法去改源代码，那么后启动的servlet的context会覆盖前面一个启动的servlet的context，对于我的应用来说，那种方式会导致action-servlet丢失。开始robbin提出的错误，是因为你在strutsPlugin里多配置了appContext，导致实际上有2分appContext的beans存在，child在自己的context里就可以找到所需要的bean，也就不会去parent里找了。StrutsPlugin里的attrName是正确合理的。 <br /><br />当然你可以把所有所有的bean全部放到root context里，这也行的通，不过本人极力反对这种方式，bean的组织太乱。 <br /><br />Spring的context是分层次的：不要把在写contextConfigLocation的时候，把你的xxx-servlet.xml路径也加进去；不要在写xxx-servlet.xml的context的时候把applicationContext的路径也配进去；不要在parent的context里引用children里的bean，不要在你的appContext里引用xxx-servlet的bean。 <br /><br />总之，就是要求你合理的、有层次的组织你的bena，而不是一陀摆出来。</span><br /><br /><br /><br /><br /><span class="postbody">applicationContext.xml如果不引用action-servlet.xml路径的话，那么action如何来引用bo； <br /></span><table cellspacing="1" cellpadding="3" width="90%" align="center" border="0"><tbody><tr><td><span class="genmed"><b>java代码: </b></span></td></tr><tr><td class="code"><div style="FONT-FAMILY: 'Courier New', Courier, monospace"><br /><br />&lt;bean name="/test" <span style="FONT-WEIGHT: bold; COLOR: #990066" ?="">class</span>="com.<span style="COLOR: #000000">xy</span>.<span style="COLOR: #000000">action</span>.<span style="COLOR: #000000">TestAction</span>"&gt; <br />  &lt;property name="testBo"&gt;&lt;ref bean="testBoProxy"/&gt;&lt;/property&gt; <br />&lt;/bean&gt; <br /></div><br /></td></tr></tbody></table><span class="postbody"><br /><br />如果bo在applicationContext.xml中的话; <br />服务器会报错,找不到bo</span> 。<br /><br /><br /><br /><br /><table cellspacing="1" cellpadding="3" width="90%" align="center" border="0"><tbody><tr><td><span class="genmed"><b>okokok 写道:</b></span></td></tr><tr><td class="quote">applicationContext.xml如果不引用action-servlet.xml路径的话，那么action如何来引用bo； <br /><table cellspacing="1" cellpadding="3" width="90%" align="center" border="0"><tbody><tr><td><span class="genmed"><b>java代码: </b></span></td></tr><tr><td class="code"><div style="FONT-FAMILY: 'Courier New', Courier, monospace"><br /><br />&lt;bean name="/test" <span style="FONT-WEIGHT: bold; COLOR: #990066" ?="">class</span>="com.<span style="COLOR: #000000">xy</span>.<span style="COLOR: #000000">action</span>.<span style="COLOR: #000000">TestAction</span>"&gt; <br />  &lt;property name="testBo"&gt;&lt;ref bean="testBoProxy"/&gt;&lt;/property&gt; <br />&lt;/bean&gt; <br /></div><br /></td></tr></tbody></table><span class="postbody"><br /><br />如果bo在applicationContext.xml中的话; <br />服务器会报错,找不到bo</span></td></tr></tbody></table><span class="postbody"><br /><br />我不太清楚你的bean的组织，在我的系统里，BO是在applicationContext之类的基础context里定义，而且工作很正常。 <br /><br />另外你需要搞清楚的是：对于Spring的BeanFactory（ApplicationContext），如果它在自己的context里找不到bean，会去parent里找。 <br /><br /></span><table cellspacing="1" cellpadding="3" width="90%" align="center" border="0"><tbody><tr><td><span class="genmed"><b>java代码: </b></span></td></tr><tr><td class="code"><div style="FONT-FAMILY: 'Courier New', Courier, monospace"><br /><br /><span style="COLOR: #6666ff">// Check if bean definition exists in this factory.</span><br />                        <span style="FONT-WEIGHT: bold; COLOR: #990066" ?="">if</span><span style="COLOR: #000000">(</span>getParentBeanFactory<span style="COLOR: #000000">(</span><span style="COLOR: #000000">)</span> != <span style="FONT-WEIGHT: bold; COLOR: #990066" ?="">null</span> &amp;&amp; !containsBeanDefinition<span style="COLOR: #000000">(</span>beanName<span style="COLOR: #000000">)</span><span style="COLOR: #000000">)</span><span style="COLOR: #000000">{</span><br />                                <span style="COLOR: #6666ff">// Not found -&gt; check parent.</span><br />                                <span style="FONT-WEIGHT: bold; COLOR: #990066" ?="">if</span><span style="COLOR: #000000">(</span>getParentBeanFactory<span style="COLOR: #000000">(</span><span style="COLOR: #000000">)</span> instanceof AbstractBeanFactory<span style="COLOR: #000000">)</span><span style="COLOR: #000000">{</span><br />                                        <span style="COLOR: #6666ff">// Delegation to parent with args only possible for AbstractBeanFactory.</span><br />                                        <span style="FONT-WEIGHT: bold; COLOR: #990066" ?="">return</span><span style="COLOR: #000000">(</span><span style="COLOR: #000000">(</span>AbstractBeanFactory<span style="COLOR: #000000">)</span> getParentBeanFactory<span style="COLOR: #000000">(</span><span style="COLOR: #000000">)</span><span style="COLOR: #000000">)</span>.<span style="COLOR: #000000">getBean</span><span style="COLOR: #000000">(</span>name, requiredType, args<span style="COLOR: #000000">)</span>; <br />                                <span style="COLOR: #000000">}</span><br />                                <span style="FONT-WEIGHT: bold; COLOR: #990066" ?="">else</span><span style="FONT-WEIGHT: bold; COLOR: #990066" ?="">if</span><span style="COLOR: #000000">(</span>args == <span style="FONT-WEIGHT: bold; COLOR: #990066" ?="">null</span><span style="COLOR: #000000">)</span><span style="COLOR: #000000">{</span><br />                                        <span style="COLOR: #6666ff">// No args -&gt; delegate to standard getBean method.</span><br />                                        <span style="FONT-WEIGHT: bold; COLOR: #990066" ?="">return</span> getParentBeanFactory<span style="COLOR: #000000">(</span><span style="COLOR: #000000">)</span>.<span style="COLOR: #000000">getBean</span><span style="COLOR: #000000">(</span>name, requiredType<span style="COLOR: #000000">)</span>; <br />                                <span style="COLOR: #000000">}</span><br />                                <span style="FONT-WEIGHT: bold; COLOR: #990066" ?="">else</span><span style="COLOR: #000000">{</span><br />                                        throw <span style="FONT-WEIGHT: bold; COLOR: #990066" ?="">new</span> NoSuchBeanDefinitionException<span style="COLOR: #000000">(</span>beanName, <br />                                                        "Cannot delegate to parent BeanFactory because it does not supported passed-in arguments"<span style="COLOR: #000000">)</span>; <br />                                <span style="COLOR: #000000">}</span><br />                        <span style="COLOR: #000000">}</span><br /></div><br /></td></tr></tbody></table><span class="postbody"><br /><br />所以无论如何，只要你在applicationContext里定义了BO，那么webApp的context一定找得到这个bean，因为applicationContext是webApp的context的parent。</span><br /><br /><br /><br /><br /><br /><span class="postbody">奇了怪了，昨天一直报找不到bo的错，今天居然没报错；服务器有问题？ <br />还有个问题，既然web.xml里可以用listener来加载applicationContext.xml，为什么还要在struts-config.xml里再用plug-in？我觉得在applicationContext.xml里按模块放置每个模块的action,bo,dao的xml文件的路径是个不错方法，比如： <br /></span><table cellspacing="1" cellpadding="3" width="90%" align="center" border="0"><tbody><tr><td><span class="genmed"><b>java代码: </b></span></td></tr><tr><td class="code"><div style="FONT-FAMILY: 'Courier New', Courier, monospace"><br /><br />&lt;?xml version="<span style="COLOR: #000000" ?="">1</span>.<span style="COLOR: #000000" ?="">0</span>" encoding="UTF-<span style="COLOR: #000000" ?="">8</span>"?&gt; <br />&lt;!DOCTYPE beans <span style="FONT-WEIGHT: bold; COLOR: #990066" ?="">PUBLIC</span> "-<span style="COLOR: #6666ff">//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"&gt;</span><br />&lt;beans&gt; <br />        &lt;<span style="FONT-WEIGHT: bold; COLOR: #990066" ?="">import</span> resource="SpringConfig/module1.<span style="COLOR: #000000">xml</span>" /&gt; <br />                &lt;<span style="FONT-WEIGHT: bold; COLOR: #990066" ?="">import</span> resource="SpringConfig/module2.<span style="COLOR: #000000">xml</span>" /&gt; <br />                &lt;<span style="FONT-WEIGHT: bold; COLOR: #990066" ?="">import</span> resource="SpringConfig/module3.<span style="COLOR: #000000">xml</span>" /&gt; <br /><br />        &lt;bean id="dataSource" <br />                <span style="FONT-WEIGHT: bold; COLOR: #990066" ?="">class</span>="org.<span style="COLOR: #000000">apache</span>.<span style="COLOR: #000000">commons</span>.<span style="COLOR: #000000">dbcp</span>.<span style="COLOR: #000000">BasicDataSource</span>"&gt; <br />                &lt;property name="driverClassName"&gt; <br />                        &lt;value&gt;org.<span style="COLOR: #000000">gjt</span>.<span style="COLOR: #000000">mm</span>.<span style="COLOR: #000000">mysql</span>.<span style="COLOR: #000000">Driver</span>&lt;/value&gt; <br />                &lt;/property&gt; <br />                &lt;property name="url"&gt; <br />                        &lt;value&gt;jdbc:mysql:<span style="COLOR: #6666ff">//localhost/airline&lt;/value&gt;</span><br />                &lt;/property&gt; <br />                &lt;property name="username"&gt; <br />                        &lt;value&gt;root&lt;/value&gt; <br />                &lt;/property&gt; <br />                &lt;property name="password"&gt; <br />                        &lt;value&gt;<span style="COLOR: #000000" ?="">123456</span>&lt;/value&gt; <br />                &lt;/property&gt; <br />        &lt;/bean&gt; <br />         ... <br />&lt;/beans&gt; <br /></div><br /></td></tr></tbody></table><span class="postbody"><br />而module1.xml的内容是： <br /><br /></span><table cellspacing="1" cellpadding="3" width="90%" align="center" border="0"><tbody><tr><td><span class="genmed"><b>java代码: </b></span></td></tr><tr><td class="code"><div style="FONT-FAMILY: 'Courier New', Courier, monospace"><br /><br />﻿&lt;?xml version="<span style="COLOR: #000000" ?="">1</span>.<span style="COLOR: #000000" ?="">0</span>" encoding="UTF-<span style="COLOR: #000000" ?="">8</span>"?&gt; <br />&lt;!DOCTYPE beans <span style="FONT-WEIGHT: bold; COLOR: #990066" ?="">PUBLIC</span> "-<span style="COLOR: #6666ff">//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"&gt;</span><br />&lt;beans&gt; <br />&lt;!-- 数据访问层 --&gt; <br />&lt;bean id="testDao" <span style="FONT-WEIGHT: bold; COLOR: #990066" ?="">class</span>="com.<span style="COLOR: #000000">xy</span>.<span style="COLOR: #000000">dao</span>.<span style="COLOR: #000000">TestDao</span>"&gt; <br />  &lt;property name="sessionFactory"&gt;&lt;ref bean="sessionFactory"/&gt;&lt;/property&gt; <br />&lt;/bean&gt; <br />&lt;!-- 业务罗基层 --&gt; <br />&lt;bean id="testBo" <span style="FONT-WEIGHT: bold; COLOR: #990066" ?="">class</span>="com.<span style="COLOR: #000000">xy</span>.<span style="COLOR: #000000">bo</span>.<span style="COLOR: #000000">TestBo</span>"&gt; <br />  &lt;property name="testDao"&gt;&lt;ref bean="testDao"/&gt;&lt;/property&gt; <br />  &lt;!-- &lt;property name="transactionManager"&gt;&lt;ref bean="transactionManager"/&gt;&lt;/property&gt; --&gt; <br />&lt;/bean&gt; <br />&lt;!-- <span style="COLOR: #aaaadd" ?="">Action</span>层 --&gt; <br />&lt;bean name="/test" <span style="FONT-WEIGHT: bold; COLOR: #990066" ?="">class</span>="com.<span style="COLOR: #000000">xy</span>.<span style="COLOR: #000000">action</span>.<span style="COLOR: #000000">TestAction</span>"&gt; <br />  &lt;property name="testBo"&gt;&lt;ref bean="testBoProxy"/&gt;&lt;/property&gt; <br />&lt;/bean&gt; <br />&lt;/beans&gt; <br /></div><br /></td></tr></tbody></table><span class="postbody"><br />这样的话不用在struts-config.xml里配置plug-in了吧</span> 。<br /><br /><br /><br /><br /><span class="postbody">struts-config.xml里配置plug-in是要配的，关键在于你Spring的配置文件的合理分层，如果像你那样什么东西都放在一个applictionContext里，那么就有可能出现象这样OpenSessioInView失效的这样“意想不到”的问题。而且会导致你错误地理解Spring，比如Spring的ApplicationEvent，本身web层的context里的Listener是听不到root层的Event的，但是你这样的配置（也是这个topic的配置）会导致Event混淆，也违背了Spring本身的设计意图。 <br /><br />PS:关于Event的问题可以看我blog，http://spaces.msn.com/sweetriver/blog/cns!367370EB9A9B2807!129.entry <br /><br />在我的配置里，各个层次都有属于自己的配置文件，messageSource同样应该有分层，为了图方便而简单吧所有bean罗列在一个配置里是不可取的，而且会导致某些设计与实现上的问题（开始我的messageSource是没有分层的，但是后来这样导致了一些非常痛苦的问题与抉择，结果还是改回分层的messageSource）。</span><br /><br /><br /><br /><br /><span class="postbody">applicationContext是有层次的，那样的方式会让struts plugIn中配置的sessionFactory比root中的sessionFactory占优，这个struts plugIn中的bean都是访问这里配置的sessionFactory。</span><br /><br /><br /><br /><span class="postbody">opensession的最大问题还是长链接的时候无法释放session的问题，在对外系统中问题尤为突出。springMVC可以仗着先天优势用interecptor，但是webwork可就没有这样的优势了，尝试自己做一个适合webwork的，但是发现一旦有安全框架介入，session作用的切面就变得难以把握。到现在还无果~~~~</span><style type="text/css"><!--
td.attachrow		{ font: normal 11px Verdana, Arial, Helvetica, sans-serif; color : #000000; border-color : #000000; }
td.attachheader     { font: normal 11px Verdana, Arial, Helvetica, sans-serif; color : #000000; border-color : #000000; background-color: #D1D7DC; }
table.attachtable	{ font: normal 12px Verdana, Arial, Helvetica, sans-serif; color : #000000; border-color : #000000;	border-collapse : collapse; }
--></style><span class="postbody"></span><span class="gensmall"></span><style type="text/css"><!--
td.attachrow		{ font: normal 11px Verdana, Arial, Helvetica, sans-serif; color : #000000; border-color : #000000; }
td.attachheader     { font: normal 11px Verdana, Arial, Helvetica, sans-serif; color : #000000; border-color : #000000; background-color: #D1D7DC; }
table.attachtable	{ font: normal 12px Verdana, Arial, Helvetica, sans-serif; color : #000000; border-color : #000000;	border-collapse : collapse; }
--></style><style type="text/css"><!--
td.attachrow		{ font: normal 11px Verdana, Arial, Helvetica, sans-serif; color : #000000; border-color : #000000; }
td.attachheader     { font: normal 11px Verdana, Arial, Helvetica, sans-serif; color : #000000; border-color : #000000; background-color: #D1D7DC; }
table.attachtable	{ font: normal 12px Verdana, Arial, Helvetica, sans-serif; color : #000000; border-color : #000000;	border-collapse : collapse; }
--></style><style type="text/css"><!--
td.attachrow		{ font: normal 11px Verdana, Arial, Helvetica, sans-serif; color : #000000; border-color : #000000; }
td.attachheader     { font: normal 11px Verdana, Arial, Helvetica, sans-serif; color : #000000; border-color : #000000; background-color: #D1D7DC; }
table.attachtable	{ font: normal 12px Verdana, Arial, Helvetica, sans-serif; color : #000000; border-color : #000000;	border-collapse : collapse; }
--></style><style type="text/css"><!--
td.attachrow		{ font: normal 11px Verdana, Arial, Helvetica, sans-serif; color : #000000; border-color : #000000; }
td.attachheader     { font: normal 11px Verdana, Arial, Helvetica, sans-serif; color : #000000; border-color : #000000; background-color: #D1D7DC; }
table.attachtable	{ font: normal 12px Verdana, Arial, Helvetica, sans-serif; color : #000000; border-color : #000000;	border-collapse : collapse; }
--></style><style type="text/css"><!--
td.attachrow		{ font: normal 11px Verdana, Arial, Helvetica, sans-serif; color : #000000; border-color : #000000; }
td.attachheader     { font: normal 11px Verdana, Arial, Helvetica, sans-serif; color : #000000; border-color : #000000; background-color: #D1D7DC; }
table.attachtable	{ font: normal 12px Verdana, Arial, Helvetica, sans-serif; color : #000000; border-color : #000000;	border-collapse : collapse; }
--></style><br /><style type="text/css"><!--
td.attachrow		{ font: normal 11px Verdana, Arial, Helvetica, sans-serif; color : #000000; border-color : #000000; }
td.attachheader     { font: normal 11px Verdana, Arial, Helvetica, sans-serif; color : #000000; border-color : #000000; background-color: #D1D7DC; }
table.attachtable	{ font: normal 12px Verdana, Arial, Helvetica, sans-serif; color : #000000; border-color : #000000;	border-collapse : collapse; }
--></style><img src ="http://www.blogjava.net/rendong/aggbug/61202.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/rendong/" target="_blank">rendong</a> 2006-08-01 14:31 <a href="http://www.blogjava.net/rendong/archive/2006/08/01/61202.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Spring让Hibernate使用者受益良多[转载]</title><link>http://www.blogjava.net/rendong/archive/2006/08/01/61188.html</link><dc:creator>rendong</dc:creator><author>rendong</author><pubDate>Tue, 01 Aug 2006 05:54:00 GMT</pubDate><guid>http://www.blogjava.net/rendong/archive/2006/08/01/61188.html</guid><wfw:comment>http://www.blogjava.net/rendong/comments/61188.html</wfw:comment><comments>http://www.blogjava.net/rendong/archive/2006/08/01/61188.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/rendong/comments/commentRss/61188.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/rendong/services/trackbacks/61188.html</trackback:ping><description><![CDATA[
		<div class="postText">Spring的轻量级的bean容器为业务对象（business objects）、DAO对象和资源（如：JDBC数据源或者Hibernate SessionFactorie等）对象提供了IoC类型的装配能力。Spring使用一个xml格式的应用配置文件为开发者提供了一种通过解析定制的属性文件来手动管理单实例对象或者工厂对象的选择性。由于Spring将非入侵性做为一个重要的目标，因此可以由Spring配置管理的bean对象均不需要依赖Spring自有的接口和类就可以通过它们的bean属性完成配置。这个概念可以被应用到任何环境中，无论你开发的是一个J2EE的web应用还是一个桌面应用甚至只是一个applet都可以。 
<p><font color="#a52a2a">    在使用Hibernate的应用中, Spring的对DAO对象通常的事务管理特别应该引起关注。它的目的就是分离数据访问和事务处理，使事务性业务对象不与任何特殊的数据访问或者事务策略绑在一起，从而不影响业务对象的可复用性。这种划分既可以经由事务模板（TransactionTemplate）用编程的方式实现，也可以经由面向方面（AOP）事务拦截器（TransactionTemplate）用声明的方式实现。无论是本地的Hibernate / JDBC事务，还是JTA事务都支持对象外的事务策略，这对于本地的无状态会话Bean（Stateless Session Beans）是一个非常有用的选择。</font></p><p><font color="#a52a2a">    Spring的HibernateTemplate类提供了一个简单的方式实现了Hibernate-based DAO对象而不必关心如何获得Hibernate的Session实例，也不必关心多方参与的事务处理。无需使用try-catch块，也无需进行事务检查。一个简单的Hibernate访问方法就完全解决了些麻烦! 无论是在多个DAO接口还是在多方事务的情况下，Spring使得多种DAO对象无缝地协同工作。例如：某些DAO对象可能是基于plain JDBC的实现，更适合于经由Spring的JdbcTemplate来避免手动的异常处理。</font></p><p><font color="#a52a2a">      你可以单独地使用许多Spring特性，因为Spring的所有对象都是设计成可复用的JavaBean对象的集合。也不要因为Spring可以提供一个完整的应该框架而气馁！使用其他的Spring特性时，应用配置概念是一个附加的特性，并不是一个必须的特性。无论如何，当你要决定去构建一个象Spring这样的内在的基础架构的时候，在使用Spring的路途上没有什么范围上的限制。</font></p><p><font color="#a52a2a"></font></p><p><strong><font color="#a52a2a">1. 介绍: 资源管理</font></strong></p><p><font color="#a52a2a">       典型的业务应用系统常常由于重复的资源管理代码而导致混乱。许多项目试着用自己的方法来解决这个问题，有时要为此付出失败的代价，Spring针对适当的资源管理提倡了一种引人注目的简单方法：即经由模板来倒置控制（Inversion of control），例如：基础类使用回调接口，或者应用AOP拦截器。其基础核心是适当的资源处理和将特殊的API异常转换为一个unchecked的基础异常。</font></p><p><font color="#a52a2a">       Spring引入了一个DAO异常层适用于任何数据访问策略。对于直接的JDBC，JdbcTemplate类关注于连接处理，并且关注于对SQLException转换为适当的DataAccessException，包括对特殊的数据库SQL错误转换为有意义的异常。 经由不同的事务管理对象，Spring支持JTA和JDBC事务。Spring 也提供对Hibernate和JDO的支持，它的这种支持由与JdbcTemplate类的作用相类似的HibernateTemplate类和JdoTemplate类, 以及HibernateInterceptor类、JdoInterceptor类，还有Hibernate、JDO 事务管理类组成。</font></p><p><font color="#a52a2a">       最主要的目的是要使应用的层次分明，为此将数据访问和事务处理同应用对象分离开来。所有的业务对象都不再依赖数据访问或者事务策略。不再有硬编码的资源查找代码，不再有难以替换的单例对象，也不再需要定制服务注册。</font></p><p><font color="#a52a2a">      所有的单独的数据访问特性均无需依赖于Spring，可以单独使用，无需让Spring知道，同时也可以通过Spring的应用配置（提供基于XML的配置和对普通JavaBean实例的交叉引用）来进行装配。在一个典型的Spring应用中，大部分重要的对象都是普通的JavaBean：数据访问模板对象（data access templates）、数据访问对象（使用数据访问模板对象的对象）、事务管理对象及业务对象（使用数据访问对象和事务对象的对象），web表示分解对象、web控制对象（使用业务对象的对象）等等。</font></p><p><strong><font color="#a52a2a">2. 应用配置中的资源定义</font></strong></p><p><font color="#a52a2a">    为了避免应用对象将资源查找的代码硬编码，Spring允许在应用配置中将一个如JDBC DataSource或者Hibernate SessionFactory定义为一个Bean。应用对象如果需要访问资源只需要通过Bean引用（DAO定义在下一部分说明）接受先前定义的实例的引用。以下的内容引用自一个应用配置定义，显示了如何建立一个JDBC DataSource和一个Hibernate的SessionFactory：</font></p><table style="BORDER-COLLAPSE: collapse" bordercolor="#cccccc" cellspacing="0" cellpadding="2" width="400" align="center" bgcolor="#cccccc" border="1"><tbody><tr><td><p><font color="#a52a2a"> &lt;beans&gt;<br /><br />&lt;bean id="myDataSource" class="org.springframework.jndi</font></p><p><font color="#a52a2a">.JndiObjectFactoryBean"&gt;<br />&lt;property name="jndiName"&gt;<br />&lt;value&gt;jdbc/myds&lt;/value&gt;<br />&lt;/property&gt;<br />&lt;/bean&gt;<br /><br />&lt;bean id="mySessionFactory" class="org.springframework.orm.hibernate</font></p><p><font color="#a52a2a">.LocalSessionFactoryBean"&gt;<br />&lt;property name="mappingResources"&gt;<br />&lt;list&gt;<br />&lt;value&gt;product.hbm.xml&lt;/value&gt;<br />&lt;/list&gt;<br />&lt;/property&gt;<br />&lt;property name="hibernateProperties"&gt;<br />&lt;props&gt;<br />&lt;prop key="hibernate.dialect"&gt;net.sf.hibernate.dialect</font></p><p><font color="#a52a2a">.MySQLDialect&lt;/prop&gt;<br />&lt;/props&gt;<br />&lt;/property&gt;<br />&lt;property name="dataSource"&gt;<br />&lt;ref bean="myDataSource"/&gt;<br />&lt;/property&gt;<br />&lt;/bean&gt;<br /><br />...<br /><br />&lt;/beans&gt;</font></p></td></tr></tbody></table><p><font color="#a52a2a">      注意选择是用JNDI来定位数据源还是从一个象Jakarta Commons DBCP BasicDataSource这样的本地定义取得一个数据源，只是一个改变配置的事：</font></p><p></p><table style="BORDER-COLLAPSE: collapse" bordercolor="#cccccc" cellspacing="0" cellpadding="2" width="400" align="center" bgcolor="#cccccc" border="1"><tbody><tr><td><p><font color="#a52a2a">&lt;bean id="myDataSource"<br />      class="org.apache.commons</font></p><p><font color="#a52a2a">.dbcp.BasicDataSource" destroy-method="close"&gt;<br />&lt;property name="driverClassName"&gt;<br />&lt;value&gt;org.hsqldb.jdbcDriver&lt;/value&gt;<br />&lt;/property&gt;<br />&lt;property name="url"&gt;<br />&lt;value&gt;jdbc:hsqldb:hsql://localhost:9001&lt;/value&gt;<br />&lt;/property&gt;<br />&lt;property name="username"&gt;<br />&lt;value&gt;sa&lt;/value&gt;<br />&lt;/property&gt;<br />&lt;property name="password"&gt;<br />&lt;value&gt;&lt;/value&gt;<br />&lt;/property&gt;<br />&lt;/bean&gt;</font></p></td></tr></tbody></table><p><font color="#a52a2a">       你也可以使用一个JNDI查找SessionFactory，但是通常对于EJB环境之外的应用来说并不是需要的（参考"container resources vs local resources"部分的讨论）。</font></p><p><strong><font color="#a52a2a">3. 倒置控制（Inversion of Control）: 模板和回调</font></strong></p><p><font color="#a52a2a">    模板的基本编程模式就象你将在下面看到那样，至于方法就如同任何定制的数据访问对象或者业务的对象的方法一样。除了需要向其提供一个Hibernate的SessionFactory之外，再没有对周围执行对象的信赖的限制。虽然最好是从一个Spring的应用配置中经由一个简单setSessionFactory bean的属性设置使用Bean引用来获得它，但随后你可以从任何地方获得它。随后的引用片段包括一段在Spring应用配置中对DAO定义的配置，其中引用了在其前面定义的SessionFactory，和一段DAO方法的实现的例子。</font></p><p></p><table style="BORDER-COLLAPSE: collapse" bordercolor="#cccccc" cellspacing="0" cellpadding="2" width="400" align="center" bgcolor="#cccccc" border="1"><tbody><tr><td><pre class="code"><font color="#a52a2a">&lt;beans&gt;

&lt;bean id="myProductDao" class="product.ProductDaoImpl"&gt;
&lt;property name="sessionFactory"&gt;
&lt;ref bean="mySessionFactory"/&gt;
&lt;/property&gt;
&lt;/bean&gt;

...

&lt;/beans&gt;</font></pre></td></tr></tbody></table><p></p><table style="BORDER-COLLAPSE: collapse" bordercolor="#cccccc" cellspacing="0" cellpadding="2" width="400" align="center" bgcolor="#cccccc" border="1"><tbody><tr><td><pre class="code"><pre class="code"><font color="#a52a2a">public class ProductDaoImpl implements ProductDao {

private SessionFactory sessionFactory;

public void setSessionFactory(SessionFactory </font></pre><pre class="code"><font color="#a52a2a">   sessionFactory) {
this.sessionFactory = sessionFactory;
}

public List loadProductsByCategory(final String </font></pre><pre class="code"><font color="#a52a2a">  category) {
HibernateTemplate hibernateTemplate =
new HibernateTemplate(this.sessionFactory);

return (List) hibernateTemplate.execute(
new HibernateCallback() {
public Object doInHibernate(Session session) throws</font></pre><pre class="code"><font color="#a52a2a"> HibernateException {
List result = session.find(
"from test.Product product where product.category=?",
category, Hibernate.STRING);
// do some further stuff with the result list
return result;
}
}
);
}
}</font></pre></pre></td></tr></tbody></table><p><font color="#a52a2a">       一个回调的实现可以被有效地用在任何Hibernate数据访问中。在任何情况下都由HibernateTemplate来管理Session的开闭和自动的多方事务。模板实例是线程安全和可重用的，因此它们可以做为其他类的变量。</font></p><p><br /><font color="#a52a2a">       对于简单的单步的动作，象find, load, saveOrUpdate或者delete的调用，HibernateTemplate提供更为便利的选择以代替象一行的回调的执行。此外，Spring提供了一个方便的基本类，就是HibernateDaoSupport类，它提供了setSessionFactory方法来接受一个SessionFactory，同时提供了getSessionFactory和getHibernateTemplate方法供其继承类使用。将这些结合起来，允许对于典型的需求给出了非常简单的DAO实现：</font></p><p></p><table style="BORDER-COLLAPSE: collapse" bordercolor="#cccccc" cellspacing="0" cellpadding="2" width="400" align="center" bgcolor="#cccccc" border="1"><tbody><tr><td><pre class="code"><pre class="code"><pre class="code"><font color="#a52a2a">public class ProductDaoImpl extends HibernateDaoSupport implements</font></pre><pre class="code"><font color="#a52a2a"> ProductDao {

public List loadProductsByCategory(String category) {
return getHibernateTemplate().find(
"from test.Product product where product.category=?", category,
Hibernate.STRING);
}
}</font></pre></pre></pre></td></tr></tbody></table><p><font color="#a52a2a"></font></p><p><strong><font color="#a52a2a">4. 应用一个AOP拦截器代替一个模板</font></strong></p><p><font color="#a52a2a">   除使用HibernateTemplate之外的另一个选择就是使用Spring的AOP HibernateInterceptor。用直接在一个委托的try/catch块中编写Hibernate代码，配合相应的在应用配置中分别的拦截器配置来代替执行回调。下面的片段显示了一个Spring应用配置中的DAO, interceptor和proxy的各自的定义，同时给出了一个DAO方法实现的例子：</font></p><p></p><table style="BORDER-COLLAPSE: collapse" bordercolor="#cccccc" cellspacing="0" cellpadding="2" width="400" align="center" bgcolor="#cccccc" border="1"><tbody><tr><td><pre class="code"><pre class="code"><pre class="code"><pre class="code"><font color="#a52a2a">&lt;beans&gt;

...

&lt;bean id="myHibernateInterceptor"
      class="org.springframework.orm.hibernate</font></pre><pre class="code"><font color="#a52a2a">.HibernateInterceptor"&gt;
&lt;property name="sessionFactory"&gt;
&lt;ref bean="mySessionFactory"/&gt;
&lt;/property&gt;
&lt;/bean&gt;

&lt;bean id="myProductDaoTarget" class="product.ProductDaoImpl"&gt;
&lt;property name="sessionFactory"&gt;
&lt;ref bean="mySessionFactory"/&gt;
&lt;/property&gt;
&lt;/bean&gt;

&lt;bean id="myProductDao" class="org.springframework.aop</font></pre><pre class="code"><font color="#a52a2a">.framework.ProxyFactoryBean"&gt;
&lt;property name="proxyInterfaces"&gt;
&lt;value&gt;product.ProductDao&lt;/value&gt;
&lt;/property&gt;
&lt;property name="interceptorNames"&gt;
&lt;list&gt;
&lt;value&gt;myHibernateInterceptor&lt;/value&gt;
&lt;value&gt;myProductDaoTarget&lt;/value&gt;
&lt;/list&gt;
&lt;/property&gt;
&lt;/bean&gt;

...

&lt;/beans&gt;</font></pre></pre></pre></pre></td></tr></tbody></table><p></p><table style="BORDER-COLLAPSE: collapse" bordercolor="#cccccc" cellspacing="0" cellpadding="2" width="400" align="center" bgcolor="#cccccc" border="1"><tbody><tr><td><pre class="code"><pre class="code"><pre class="code"><font color="#a52a2a"> </font><pre class="code"><font color="#a52a2a">public class ProductDaoImpl extends HibernateDaoSupport </font></pre><pre class="code"><font color="#a52a2a">   implements ProductDao {

public List loadProductsByCategory(final String category)</font></pre><pre class="code"><font color="#a52a2a"> throws MyException {
Session session = SessionFactoryUtils</font></pre><pre class="code"><font color="#a52a2a">.getSession(getSessionFactory(), false);
try {
List result = session.find(
"from test.Product product where product.category=?",
category, Hibernate.STRING);
if (result == null) {
throw new MyException("invalid search result");
}
return result;
}
catch (HibernateException ex) {
throw SessionFactoryUtils.convertHibernateAccessException(ex);
}
}
}
</font></pre></pre></pre></pre></td></tr></tbody></table><p><font color="#a52a2a">        这个方法将只在有一个与它配合的HibernateInterceptor时才能正常工作，HibernateInterceptor为它负责在方法调用前线程绑定Session的开启和方法调用后的关闭。getSession方法调用中的"false"标志是要确认Session必须是已经存在的，如果没有发现任何一个Session，SessionFactoryUtils将会为其创建一个。如果已经有一个Session句柄绑定在本线程上，比如是由一个HibernateTransactionManager事务绑定的，在任何情况下SessionFactoryUtils会自动接入这个Session。HibernateTemplate在底层也使用SessionFactoryUtils，与以上说的方式基本是一样的。</font></p><p><font color="#a52a2a">       HibernateInterceptor的主要益处是它允许在数据访问代码中抛出checked application exception，而HibernateTemplate由于受限于回调只能在其中抛出unchecked exceptions。注意到这点我们可以推迟各自的检验，同时在回调后抛出应用异常。拦截方式的主要缺点是它需要在配置中进行特殊的配置。HibernateTemplate在大多数情况下都是一种简单好用的方法。<br /></font></p><p><strong><font color="#a52a2a">5. 程序事务划分</font></strong></p><p><font color="#a52a2a">   在这种底层的数据访问服务之上，事务处理可以在更高的应用层被划分 ，形成一些操作。这里除了需要一个Spring的PlatformTransactionManager对象外，对于周围运行的业务对象也没有任何限制。同样的，其后你可以从任何地方获得它们，但是经由Bean引用的方式通过setTransactionManage方法获得更为适合，象productDAO要经由一个setProductDao方法获得一样。下面的引用片段显示了在一个Spring应用配置中的事务管理对象和业务对象的定义，并且还提供了一个业务方法实现的例子：</font></p><p></p><table style="BORDER-COLLAPSE: collapse" bordercolor="#cccccc" cellspacing="0" cellpadding="2" width="400" align="center" bgcolor="#cccccc" border="1"><tbody><tr><td><pre class="code"><pre class="code"><pre class="code"><pre class="code"><font color="#a52a2a">&lt;beans&gt;

...

&lt;bean id="myTransactionManager"
      class="org.springframework.orm.hibernate</font></pre><pre class="code"><font color="#a52a2a">.HibernateTransactionManager"&gt;
&lt;property name="sessionFactory"&gt;
&lt;ref bean="mySessionFactory"/&gt;
&lt;/property&gt;
&lt;/bean&gt;

&lt;bean id="myProductService" class="product.ProductServiceImpl"&gt;
&lt;property name="transactionManager"&gt;
&lt;ref bean="myTransactionManager"/&gt;
&lt;/property&gt;
&lt;property name="productDao"&gt;
&lt;ref bean="myProductDao"/&gt;
&lt;/property&gt;
&lt;/bean&gt;

&lt;/beans&gt;</font></pre></pre></pre></pre></td></tr></tbody></table><p></p><table style="BORDER-COLLAPSE: collapse" bordercolor="#cccccc" cellspacing="0" cellpadding="2" width="400" align="center" bgcolor="#cccccc" border="1"><tbody><tr><td><pre class="code"><pre class="code"><pre class="code"><font color="#a52a2a"> </font><pre class="code"><font color="#a52a2a">public class ProductServiceImpl implements ProductService {

private PlatformTransactionManager transactionManager;
private ProductDao productDao;

public void setTransactionManager(PlatformTransactionManager </font></pre><pre class="code"><font color="#a52a2a">  transactionManager) {
this.transactionManager = transactionManager;
}

public void setProductDao(ProductDao productDao) {
this.productDao = productDao;
}

public void increasePriceOfAllProductsInCategory(final String </font></pre><pre class="code"><font color="#a52a2a">  category) {
TransactionTemplate transactionTemplate =
new TransactionTemplate(this.transactionManager);
transactionTemplate.setPropagationBehavior(TransactionDefinition</font></pre><pre class="code"><font color="#a52a2a">    .PROPAGATION_REQUIRED);
transactionTemplate.execute(
new TransactionCallbackWithoutResult() {
public void doInTransactionWithoutResult(TransactionStatus </font></pre><pre class="code"><font color="#a52a2a">  status) {
List productsToChange = productDAO.loadProductsByCategory(category);
...
}
}
);
}
}</font></pre></pre></pre></pre></td></tr></tbody></table><p><strong><font color="#a52a2a">6. 声明性事务划分</font></strong></p><p><font color="#a52a2a">       我们还可以选择使用Spring的AOP TransactionInterceptor通过在应用配置中定义拦截器配置来代替事务划分代码的事务处理方式。这允许我们保持业务对象独立于每个业务对象中重复的事务划分代码。此外，事务行为和隔离层次的变化可以通过一个配置文件来改变而不需要对业务对象的实现造成影响。</font></p><p></p><table style="BORDER-COLLAPSE: collapse" bordercolor="#cccccc" cellspacing="0" cellpadding="2" width="400" align="center" bgcolor="#cccccc" border="1"><tbody><tr><td><pre class="code"><pre class="code"><font color="#a52a2a">&lt;beans&gt;

...

&lt;bean id="myTransactionManager"
      class="org.springframework.orm.hibernate</font></pre><pre class="code"><font color="#a52a2a">.HibernateTransactionManager"&gt;
&lt;property name="sessionFactory"&gt;
&lt;ref bean="mySessionFactory"/&gt;
&lt;/property&gt;
&lt;/bean&gt;

&lt;bean id="myTransactionInterceptor"
      class="org.springframework.transaction</font></pre><pre class="code"><font color="#a52a2a">.interceptor.TransactionInterceptor"&gt;
&lt;property name="transactionManager"&gt;
&lt;ref bean="myTransactionManager"/&gt;
&lt;/property&gt;
&lt;property name="transactionAttributeSource"&gt;
&lt;value&gt;
product.ProductService.increasePrice*=PROPAGATION_REQUIRED
product.ProductService.someOtherBusinessMethod=</font></pre><pre class="code"><font color="#a52a2a">PROPAGATION_MANDATORY
&lt;/value&gt;
&lt;/property&gt;
&lt;/bean&gt;

&lt;bean id="myProductServiceTarget" class="product</font></pre><pre class="code"><font color="#a52a2a">.ProductServiceImpl"&gt;
&lt;property name="productDao"&gt;
&lt;ref bean="myProductDao"/&gt;
&lt;/property&gt;
&lt;/bean&gt;

&lt;bean id="myProductService" class="org.springframework.aop</font></pre><pre class="code"><font color="#a52a2a">.framework.ProxyFactoryBean"&gt;
&lt;property name="proxyInterfaces"&gt;
&lt;value&gt;product.ProductService&lt;/value&gt;
&lt;/property&gt;
&lt;property name="interceptorNames"&gt;
&lt;list&gt;
&lt;value&gt;myTransactionInterceptor&lt;/value&gt;
&lt;value&gt;myProductServiceTarget&lt;/value&gt;
&lt;/list&gt;
&lt;/property&gt;
&lt;/bean&gt;

&lt;/beans&gt;</font></pre></pre></td></tr></tbody></table><p></p><table style="BORDER-COLLAPSE: collapse" bordercolor="#cccccc" cellspacing="0" cellpadding="2" width="400" align="center" bgcolor="#cccccc" border="1"><tbody><tr><td><pre class="code"><font color="#a52a2a"> </font><pre class="code"><font color="#a52a2a">public class ProductServiceImpl implements ProductService {

private ProductDao productDao;

public void setProductDao(ProductDao productDao) {
this.productDao = productDao;
}

public void increasePriceOfAllProductsInCategory(final String</font></pre><pre class="code"><font color="#a52a2a"> category) {
List productsToChange = this.productDAO</font></pre><pre class="code"><font color="#a52a2a">.loadProductsByCategory(category);
...
}
}</font></pre></pre></td></tr></tbody></table><p><font color="#a52a2a">      如同使用HibernateInterceptor一样，TransactionInterceptor允许任何checked application exception从回调代码中抛出，而TransactionTemplate受回调限制在其内部抛出unchecked exceptions，在出现一个unchecked application exception的情况时，TransactionTemplate将引发一个回滚或者这个事务由应用（通过事务状态）标记为回滚。TransactionInterceptor默认情况也是同样的行为，但是允许为每一个方法制定回滚策略。<br />       建立声明性事务的一个便利的方式是使用TransactionProxyFactoryBean，特别是如果没有其他AOP拦截器的话，TransactionProxyFactoryBean将联合定义为代理的自身与一个特殊的目标Bean的事务配置。这将减少一个代理Bean对应一个目标Bean的配置情况。此外，你不必指定哪个接口或者哪个类必须定义事务方法。</font></p><p></p><table style="BORDER-COLLAPSE: collapse" bordercolor="#cccccc" cellspacing="0" cellpadding="2" width="400" align="center" bgcolor="#cccccc" border="1"><tbody><tr><td><pre class="code"><pre class="code"><pre class="code"><pre class="code"><font color="#a52a2a">&lt;beans&gt;

...

&lt;bean id="myTransactionManager"
      class="org.springframework.orm.hibernate</font></pre><pre class="code"><font color="#a52a2a">.HibernateTransactionManager"&gt;
&lt;property name="sessionFactory"&gt;
&lt;ref bean="mySessionFactory"/&gt;
&lt;/property&gt;
&lt;/bean&gt;

&lt;bean id="myProductServiceTarget" class="product</font></pre><pre class="code"><font color="#a52a2a">.ProductServiceImpl"&gt;
&lt;property name="productDao"&gt;
&lt;ref bean="myProductDao"/&gt;
&lt;/property&gt;
&lt;/bean&gt;

&lt;bean id="myProductService"
      class="org.springframework.transaction.interceptor</font></pre><pre class="code"><font color="#a52a2a">.TransactionProxyFactoryBean"&gt;
&lt;property name="transactionManager"&gt;
&lt;ref bean="myTransactionManager"/&gt;
&lt;/property&gt;
&lt;property name="target"&gt;
&lt;ref bean="myProductServiceTarget"/&gt;
&lt;/property&gt;
&lt;property name="transactionAttributes"&gt;
&lt;props&gt;
&lt;prop key="increasePrice*"&gt;PROPAGATION_REQUIRED&lt;/prop&gt;
&lt;prop key="someOtherBusinessMethod"&gt;PROPAGATION_MANDATORY&lt;/prop&gt;
&lt;/props&gt;
&lt;/property&gt;
&lt;/bean&gt;

&lt;/beans&gt;
</font></pre></pre></pre></pre></td></tr></tbody></table><p><strong><font color="#a52a2a">7. 事务管理策略</font></strong></p><p><font color="#a52a2a">          对于Hibernate应用来说，无论是TransactionTemplate还是TransactionInterceptor都是委托验实际的事务处理给PlatformTransactionManager实例，可以是一个HibernateTransactionManager（由一个单一的Hibernate的SessionFactory，使用一个ThreadLocal Session）或者可以是一个JtaTransactionManager（代理容器的JTA子系统）。甚至你可以使用一个自定义的PlatformTransactionManager实现。<br />       如果选择从本地Hibernate事务管理转为由JTA来进行事务管理，例如：当你的应用的部署面对分布的事务需求时，也仅仅是改变一下配置的事。只要简单地将Hibernate的事务管理换为JTA事务实现即可。所有的事务划分和数据访问无需做任何变动仍可以继续工作，因为他们使用的都是普通的事务管理API。<br />       对于分布式的事务会跨越多个Hibernate的session factories，仅仅是联合JtaTransactionManager与多个LocalSessionFactoryBean定义作为事务策略。你的每一个DAO将通过它们各自的Bean属性得到一个特殊的SessionFactory的引用。如果这一切都是在下面的JDBC数据源是事务容器，一个业务对象可以划分事务跨越很多DAO和很多session factories而无需做特别的处理，对于使用JtaTransactionManager做为事务策略也是一样的。</font></p><p></p><table style="BORDER-COLLAPSE: collapse" bordercolor="#cccccc" cellspacing="0" cellpadding="2" width="400" align="center" bgcolor="#cccccc" border="1"><tbody><tr><td><pre class="code"><pre class="code"><pre class="code"><font color="#a52a2a"> </font><pre class="code"><font color="#a52a2a">&lt;beans&gt;

&lt;bean id="myDataSource1" class="org.springframework.jndi</font></pre><pre class="code"><font color="#a52a2a">.JndiObjectFactoryBean"&gt;
&lt;property name="jndiName"&gt;
&lt;value&gt;jdbc/myds1&lt;/value&gt;
&lt;/property&gt;
&lt;/bean&gt;

&lt;bean id="myDataSource2" class="org.springframework.jndi.</font></pre><pre class="code"><font color="#a52a2a">JndiObjectFactoryBean"&gt;
&lt;property name="jndiName"&gt;
&lt;value&gt;jdbc/myds2&lt;/value&gt;
&lt;/property&gt;
&lt;/bean&gt;

&lt;bean id="mySessionFactory1"
      class="org.springframework.orm.hibernate.</font></pre><pre class="code"><font color="#a52a2a">LocalSessionFactoryBean"&gt;
&lt;property name="mappingResources"&gt;
&lt;list&gt;
&lt;value&gt;product.hbm.xml&lt;/value&gt;
&lt;/list&gt;
&lt;/property&gt;
&lt;property name="hibernateProperties"&gt;
&lt;props&gt;
&lt;prop key="hibernate.dialect"&gt;net.sf.hibernate.dialect.</font></pre><pre class="code"><font color="#a52a2a">MySQLDialect&lt;/prop&gt;
&lt;/props&gt;
&lt;/property&gt;
&lt;property name="dataSource"&gt;
&lt;ref bean="myDataSource1"/&gt;
&lt;/property&gt;
&lt;/bean&gt;

&lt;bean id="mySessionFactory2"
      class="org.springframework.orm.hibernate.</font></pre><pre class="code"><font color="#a52a2a">LocalSessionFactoryBean"&gt;
&lt;property name="mappingResources"&gt;
&lt;list&gt;
&lt;value&gt;inventory.hbm.xml&lt;/value&gt;
&lt;/list&gt;
&lt;/property&gt;
&lt;property name="hibernateProperties"&gt;
&lt;props&gt;
&lt;prop key="hibernate.dialect"&gt;net.sf.hibernate.</font></pre><pre class="code"><font color="#a52a2a">dialect.OracleDialect&lt;/prop&gt;
&lt;/props&gt;
&lt;/property&gt;
&lt;property name="dataSource"&gt;
&lt;ref bean="myDataSource2"/&gt;
&lt;/property&gt;
&lt;/bean&gt;

&lt;bean id="myTransactionManager"
      class="org.springframework.transaction.jta.</font></pre><pre class="code"><font color="#a52a2a">JtaTransactionManager"/&gt;

&lt;bean id="myProductDao" class="product.ProductDaoImpl"&gt;
&lt;property name="sessionFactory"&gt;
&lt;ref bean="mySessionFactory1"/&gt;
&lt;/property&gt;
&lt;/bean&gt;

&lt;bean id="myInventoryDao" class="product.InventoryDaoImpl"&gt;
&lt;property name="sessionFactory"&gt;
&lt;ref bean="mySessionFactory2"/&gt;
&lt;/property&gt;
&lt;/bean&gt;

&lt;bean id="myProductServiceTarget" class="product.</font></pre><pre class="code"><font color="#a52a2a">ProductServiceImpl"&gt;
&lt;property name="productDao"&gt;
&lt;ref bean="myProductDao"/&gt;
&lt;/property&gt;
&lt;property name="inventoryDao"&gt;
&lt;ref bean="myInventoryDao"/&gt;
&lt;/property&gt;
&lt;/bean&gt;

&lt;bean id="myProductService"
      class="org.springframework.transaction.interceptor.</font></pre><pre class="code"><font color="#a52a2a">TransactionProxyFactoryBean"&gt;
&lt;property name="transactionManager"&gt;
&lt;ref bean="myTransactionManager"/&gt;
&lt;/property&gt;
&lt;property name="target"&gt;
&lt;ref bean="myProductServiceTarget"/&gt;
&lt;/property&gt;
&lt;property name="transactionAttributes"&gt;
&lt;props&gt;
&lt;prop key="increasePrice*"&gt;PROPAGATION_REQUIRED&lt;/prop&gt;
&lt;prop key="someOtherBusinessMethod"&gt;PROPAGATION_MANDATORY&lt;/prop&gt;
&lt;/props&gt;
&lt;/property&gt;
&lt;/bean&gt;

&lt;/beans&gt;</font></pre></pre></pre></pre></td></tr></tbody></table><p><font color="#a52a2a">       无论是HibernateTransactionManager还是JtaTransactionManager允许适当的对Hibernate的在JVM层次的缓存处理-不需要容器-提供特殊的事务查找或者JCA连接器（只要不使用EJB发起事务）。另外，HibernateTransactionManager能输出JDBC连接供通常的JDBC访问代码使用。这样就允许在高层次上的事务划分是混合了Hibernate与JDBC而不要JTA的，只要只是访问一个数据库就可以！</font></p><p><strong><font color="#a52a2a">8. 使用Spring管理应用的Bean</font></strong></p><p><font color="#a52a2a">        一个Spring应用配置定义可以被多种配置实现所加载，从FileSystemXmlApplicationContext和ClassPathXmlApplicationContext到XmlWebApplicationContext。这就允许在各种环境下重用Spring管理的数据访问和业务对象。默认情况下，一个Web应用将有它自己的定义在“WEB-INF/applicationContext.xml”中的根配置。<br />       在任何一个Spring应用中，一个应用配置定义在一个XML格式的文件中用来对应用的所有有关的Bean进行装配，从Hibernate的session factory到自定义的数据访问和业务对象（象上面所有的Bean那样）。他们中的大多数不需要Spring容器知道他们，甚至即使是与其他Bean合作时也一样，因为他们只是简单的JavaBean之间的协作。下面的Bean定义可能是一个Spring Web 的MVC配置中用来访问业务对象的配置的一部分。</font></p><p></p><table style="BORDER-COLLAPSE: collapse" bordercolor="#cccccc" cellspacing="0" cellpadding="2" width="400" align="center" bgcolor="#cccccc" border="1"><tbody><tr><td><pre class="code"><font color="#a52a2a"> </font><pre class="code"><pre class="code"><font color="#a52a2a">&lt;bean id="myProductList" class="product.ProductListController"&gt;
&lt;property name="productService"&gt;
&lt;ref bean="myProductService"/&gt;
&lt;/property&gt;
&lt;/bean&gt;</font></pre></pre></pre></td></tr></tbody></table><p><font color="#a52a2a">      Spring的Web控制器经由Bean引用拥有它们需要的所有的业务和数据访问对象，因此它们无需在应用配置中做任何手工的Bean查找。但是当使用Spring管理的Beans用于Struts或者是在EJB实现，或者一个applet中时常常是需要必须手工查找一个Bean的。因此Spring的Bean可以被用在任何地方。也许只是需要是一应用配置的引用，或者经由一个web容器的Servlet配置属性，或者从一个文件中或者类路径的资源中创建它。</font></p><p></p><table style="BORDER-COLLAPSE: collapse" bordercolor="#cccccc" cellspacing="0" cellpadding="2" width="400" align="center" bgcolor="#cccccc" border="1"><tbody><tr><td><pre class="code"><pre class="code"><font color="#a52a2a">ApplicationContext context =WebApplicationContextUtils.</font></pre><pre class="code"><font color="#a52a2a">getWebApplicationContext(servletContext);
ProductService productService =
(ProductService) context.getBean("myProductService");</font></pre></pre></td></tr></tbody></table><p></p><table style="BORDER-COLLAPSE: collapse" bordercolor="#cccccc" cellspacing="0" cellpadding="2" width="400" align="center" bgcolor="#cccccc" border="1"><tbody><tr><td><pre class="code"><font color="#a52a2a"> ApplicationContext context =
new FileSystemXmlApplicationContext("C:/myContext.xml");
ProductService productService =
(ProductService) context.getBean("myProductService");
</font></pre></td></tr></tbody></table><p></p><table style="BORDER-COLLAPSE: collapse" bordercolor="#cccccc" cellspacing="0" cellpadding="2" width="400" align="center" bgcolor="#cccccc" border="1"><tbody><tr><td><pre class="code"><font color="#a52a2a"> ApplicationContext context =
new ClassPathXmlApplicationContext("myContext.xml");
ProductService productService =
(ProductService) context.getBean("myProductService");</font></pre></td></tr></tbody></table><p><strong><font color="#a52a2a">9. 容器资源VS本地资源</font></strong></p><p><font color="#a52a2a">       Spring的资源管理允许简单地在一个JNDI SessionFactory和一个本地SessionFactory间做选择，同样允许在一个JNDI DataSource与本地DataSource间做选择，而无需改变应用的一行代码。在容器中保存资源定义还是在应用本地保存，主要是一个事务策略方面的事。比较一个Spring定义的本地SessionFactory与一个手工注册的JNDI SessionFactory没有任何益处。如果经由Hibernate的JCA连接器注册，才会有加入JTA事务的明显益处，特别是对EJB。</font></p><p><font color="#a52a2a">       一个重要的Spring事务提供的好处是它不与任何容器绑定。定义包括JTA在内的策略，它都可以独立工作或者在一个试验环境中工作。特别是对典型的一个数据库的事务来说，对于JTA这是一个非常轻量的和强大的选择。当使用本地EJB SLSB的事务时，你将同时依赖EJB容器和JTA-即使你只是访问一个数据库，即使只是使用SLSBs经由CMT来声明事务。选择使用 JTA编程也需要一个J2EE环境。</font></p><p><font color="#a52a2a">       就JTA自身和JNDI数据源来说JTA不只是包括容器依赖。对于不使用Spring的JTA驱动的Hibernate事务，你必须使用HibernateJCA连接器或者在合适的JVM缓冲层专门写Hibernate的事务代码配置JTA事务。在只访问一个数据库的情况下，Spring驱动的事务可以与一个本地定义的Hibernate的SessionFactory配合良好，就如同与一个本地JDBC数据源相配合一样。因此当面对分布的事务需求时，你只需要转换为Spring的JTA事务策略即可。 </font></p><p><font color="#a52a2a">       要注意一个JCA连接器需要特别的容器的部署步骤，并且显然首先得支持JCA。这比使用本地资源定义和Spring驱动事务来部署一个简单的Web应用有更多的争议。而且你常常需要企业版本的容器支持，象WebLogic Express就不提供JCA。一个只用一个数据库的使用本地资源和事务的Spring应用可以在任何J2EE的Web容器中工作，Web容器不必支持JTA, JCA和EJB，如：Tomcat, Resin甚至最小的Jetty。另外，这样一个中间层就可以很容易地在桌面应用或者在测试套件中被重用。</font></p><p><font color="#a52a2a">       所有考虑过的事情包括：如果你不使用EJB，坚持使用本地SessionFactory，使用SpringHibernateTransactionManager或者JtaTransactionManager，你将获得包括适当处理的JVM层的缓存和分布事务的所有益处，而无需引起任何关于容器部署的争论。经由JCA连接器的一个Hibernate的SessionFactory的JNDI注册只是在使用EJB的情况中才会有明显的附加值。</font></p><p><br /><strong><font color="#a52a2a">10. Skeletons和例子</font></strong></p><p><font color="#a52a2a">      配置使用Spring和HIbernate的一个J2EE的Web应用的注释和细节最好去看看在Spring Framework的例子中的“典型的Web应用”Skeletons，它给出了适合于JDBC 和 Hibernate应用的多种数据源及事务管理的配置项，仔细看一下事务拦截器的配置，它也同样向你展示了如何配置AOP拦截器。</font></p><p><font color="#a52a2a">      在Spring的1.0 M2版中，例子Petclinic提供了JDBC和Hibernate的DAO实现和应用配置的选择。Petclinic<br />可以作为一个可工作的简单应用说明如何在一个Spring web 应用中使用Hibernate，同样也包括根据不同的事务策略来声明事务划分。<br /></font></p><h2><font color="#a52a2a">Links</font></h2><p><a href="http://www.springframework.org/" target="_blank"><font color="#a52a2a">Spring Framework website</font></a></p><p><a href="http://www.springframework.org/documentation.html" target="_blank"><font color="#a52a2a">Spring Framework documentation</font></a></p></div>
<img src ="http://www.blogjava.net/rendong/aggbug/61188.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/rendong/" target="_blank">rendong</a> 2006-08-01 13:54 <a href="http://www.blogjava.net/rendong/archive/2006/08/01/61188.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>