在Spring配置文件中使用基于schema的事务控制:
<!-- 事务管理器配置 -->
<bean id="txManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<!-- 事务通知配置:当遇到业务异常(BaseBizException)时回滚 -->
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="*"
rollback-for="com.jec.myframework.base.BaseBizException" />
</tx:attributes>
</tx:advice>
<aop:config>
<!-- 对com.jec.myframework.services下所有业务接口中的公开业务方法使用事务 -->
<aop:advisor advice-ref="txAdvice"
pointcut="execution(public * com.jec.myframework.services..*Service.*(..))" />
</aop:config>
测试PurchaseOrderService接口的testTX()方法:
public interface PurchaseOrderService { public void testTx() throws BaseBizException; public void printDate();
}public class PurchaseOrderServiceImpl implements PurchaseOrderService { private PurchaseOrderDAO poDAO; public void setPoDAO(PurchaseOrderDAO poDAO) {
this.poDAO = poDAO;
}

@Override
public void testTx() throws BaseBizException { PurchaseOrder po1 = poDAO.findById(11L);

System.out.println(po1.getPosos().size());

}
@Override
public void printDate() {
System.out.println(new Date()); }}
测试成功。
但是,当我加上另一个aop切面时,原来的测试却提示no session or session was closed。似乎是没有被事务包围。
<aop:config>
<aop:aspect ref="poService">
<aop:after method="printDate"
pointcut="execution(* com.jec.myframework.services.SalesOrderService.getAllSalesOrders(..))" />
</aop:aspect>
</aop:config>而这个切面的功能本身是可以正常工作的。去掉这个切面原来的测试就OK了。也用DEBUG模式看过,看不出所以然。这是为什么呢?

解决方案 »

  1.   

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="
           http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
           http://www.springframework.org/schema/tx
           http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
           http://www.springframework.org/schema/aop
           http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
        
    <bean id="dataSource"
    class="org.apache.commons.dbcp.BasicDataSource">
    <property name="driverClassName"
    value="oracle.jdbc.driver.OracleDriver">
    </property>
    <property name="url"
    value="jdbc:oracle:thin:@127.0.1.130.18:1521:revic">
    </property>
    <property name="username" value="aa"></property>
    <property name="password" value="aaa"></property>
    </bean>

    <!-- 
    <bean id="dataSource"
    class="org.apache.commons.dbcp.BasicDataSource">
    <property name="driverClassName"
    value="oracle.jdbc.driver.OracleDriver">
    </property>
    <property name="url"
    value="jdbc:oracle:thin:@127.0.0.1:1521:yang">
    </property>
    <property name="username" value="yang"></property>
    <property name="password" value="yang"></property>
    </bean>
    -->

    <bean id="sessionFactory"
    class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
    <property name="dataSource">
    <ref bean="dataSource" />
    </property>
    <property name="hibernateProperties">
    <props>
    <prop key="hibernate.dialect">
          org.hibernate.dialect.OracleDialect
    </prop>
    <prop key="hibernate.show_sql">true</prop>
    </props>
    </property>
    <property name="mappingLocations">
    <value>classpath:/com/revic/lms/*/po/*.hbm.xml</value>
    </property>
    <property name="lobHandler">
    <ref bean="lobHandler" />
    </property>
    </bean>
    <bean id="nativeJdbcExtractor" lazy-init="true"  class="org.springframework.jdbc.support.nativejdbc.SimpleNativeJdbcExtractor"/> <bean id="lobHandler" lazy-init="true"   class="org.springframework.jdbc.support.lob.OracleLobHandler">
            <property name="nativeJdbcExtractor">
                <ref bean="nativeJdbcExtractor"/>
            </property>
    </bean> 
    <!-- 配置事务管理器 -->
    <bean id="transactionManager"
    class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory">
    <ref bean="sessionFactory" />
    </property>
    </bean> <!-- 配置事务的传播特性 -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        
    <tx:method name="add*" propagation="REQUIRED" />
    <tx:method name="del*" propagation="REQUIRED" />
    <tx:method name="modify*" propagation="REQUIRED" />
    <tx:method name="update*" propagation="REQUIRED" />
    <tx:method name="*" read-only="true" />
    </tx:attributes>
    </tx:advice> <!-- 那些类的哪些方法参与事务 -->
    <aop:config>
    <aop:pointcut id="allManagerMethod"
    expression="execution(* com.revic.lms.*.*(..))" />
    <aop:advisor pointcut-ref="allManagerMethod"
    advice-ref="txAdvice" />
    </aop:config>
    </beans>
      

  2.   

    1.这里是些AOP的资料,楼主看看能不能换 <aop:after> to <aop:after-returning> or <aop:after-throwing>AOP configuration element Purpose
    <aop:advisor> Defines an AOP advisor.
    <aop:after> Defines an AOP after advice (regardless of whether the 
    advised method returns successfully).
    <aop:after-returning> Defines an AOP after-returning advice.
    <aop:after-throwing> Defines an AOP after-throwing advice.
    <aop:around> Defines an AOP around advice.
    <aop:aspect> Defines an aspect.
    <aop:before> Defines an AOP before advice.
    <aop:config> The top-level AOP element. Most  <aop:*> elements must 
    be contained within <aop:config>.
    <aop:pointcut> Defines a pointcut.2. 关于SESSION 问题, txManager 里边已经注入一个SESSION, 楼主第一次定义的AOP用到这段代码:
    <aop:config>
            <!-- 对com.jec.myframework.services下所有业务接口中的公开业务方法使用事务 -->
            <aop:advisor advice-ref="txAdvice"
    这个txAdvice被注入到了aop:config里边去了,那么也就是说第一次的aop:config肯定有SESSION,而第二次楼主单独定义一个切面, 这是要考虑把 txManager注入进去,这样第二个切面就会有SESSION了。
    照着这个思路发展, 相信楼主可以解决问题,在Spring  in Action这本书中有些介绍,楼主可以翻阅一下。
      

  3.   

    sessionFactory以及数据库连接方面的配置都已经定义了,只是这里省略了。
      

  4.   

    那么也就是说,所有的<aop:aspect>和<aop:advice>都写在一个<aop:config>里面就行了么?我这样试了一下,还是不行。在没有添加
            <aop:aspect ref="poService">
                <aop:after method="printDate"
                    pointcut="execution(* com.jec.myframework.services.SalesOrderService.getAllSalesOrders(..))" />
            </aop:aspect>
    这个切面的情况下,测试PurchaseOrderImpl中的testTX()方法是没有问题的(在事务里面);添加了这个切面之后就出错了(没有被事务包围,导致no session or session was closed)。这个切面只是在执行另一个SalesOrderService的getAllSalesOrders()方法之后调用PurchaseOrderImpl中的printDate()方法,和testTX()方法没有直接关系。而且我单独测试SalesOrderService.getAllSalesOrders()的话,切面工作正常,printDate预期执行。
      

  5.   

    由于hibernate延迟加载的特性可能导致在session关闭后执行sql,一般使用声明式事务交给spring管理session是不会出现这个问题的,但如果配置事务错误导致无法动态切入进行事务管理就有可能了
    Spring提供Open Session In View来解决这个问题, 有两种方式
    1. Interceptor     <!-- =========== OpenSession In View pattern ==============-->
        <bean id="openSessionInViewInterceptor"            class="org.springframework.orm.hibernate3.support.OpenSessionInViewInterceptor">
            <property name="sessionFactory" ref="sessionFactory"/>
        </bean>    <bean id="urlMapping"  class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
            <property name="interceptors" ref="openSessionInViewInterceptor"/>
            <property name="mappings">
                <props>
                ......
                </props>
            </property>
        </bean>
        
    2. Filter <web-app>
    <filter>
    <filter-name>hibernateFilter</filter-name>
    <filter-class>
    org.springframework.orm.hibernate.support.OpenSessionInViewFilter
    </filter-class>
    </filter>
     
    <filter-mapping>
    <filter-name>hibernateFilter</filter-name>
    <url-pattern>*.do</url-pattern>
    </filter-mapping></web-app>
      

  6.   


    我的问题还没有涉及到视图层(View),好像和OSIV没有关系吧。
      

  7.   


    用于匹配的aspectj表达式完全合格,因为我在添加新的切面前事务运行正常;而单独测试新切面的pointcut所指定的方法时,新切面也成功运行。
    对于遇到什么异常而使事务回滚,这个可以在配置中指定,Spring默认是遇到未受检(unchecked)异常时事务回滚,而我在我的配置中指定遇到任何业务异常(继承自BaseBizException,这个是自己定义的)时事务回滚:
    <!-- 事务通知配置:当遇到业务异常(BaseBizException)时回滚 -->
        <tx:advice id="txAdvice" transaction-manager="txManager">
            <tx:attributes>
                <tx:method name="*"
                    rollback-for="com.jec.myframework.base.BaseBizException" />
            </tx:attributes>
        </tx:advice>所以我在我的被测试方法中就会指定它抛出此类异常(throw new BaseBizException),添加新切面前遇到这样的异常事务是回滚的,添加新切面之后事务就不回滚了(从DEBUG模式中看到根本就没有创建事务)。把我遇到的这个问题简化一下:假设我有一个切面类MyAspectService,其中有两个业务方法a()和b(),而且这两个方法都被指定在事务中运行:
    public class MyAspectService{
        public void a(){}
        public void b(){}
    }
    另外有一个业务类MyBizService,其中有方法c(),而且在执行方法c()之前必须先执行切面类MyAspectService中的方法a(),那么该切面用基于schema的配置方式作如下配置:
    <aop:aspect ref="myAspectService">
                <aop:before method="a"
                    pointcut="execution(* MyBizService.c(..))" />
            </aop:aspect>
        
           <bean id="myAspectService" class="MyAspectService"/>在做了上述配置之后,单独测试MyBizService的方法c(),切面成功执行(MyAspectService的方法a()成功之行)。但是如果这时去单独测试方法b(),原先包围b()的事务却失效了(b()和切面MyAspectService的执行没有直接的关系);去掉<aop:aspect ref="myAspectService">...</aop:aspect>这一段,包围b()的事务正常工作。
      

  8.   

    你说的b()方法是一个aop方法,既然是aop方法,为何要单独执行呢
    怀疑你的设计有些问题,或者我理解的有错误
      

  9.   

    顺便,贴一下spring中的关于多个通知的顺序问题,事务是一个aop,又自定义一个aop,执行顺序会有一些问题
    说明如下:
    6.2.4.7. 通知顺序
    如果有多个通知想要在同一连接点运行会发生什么?Spring AOP遵循跟AspectJ一样的优先规则来确定通知执行的顺序。 在“进入”连接点的情况下,最高优先级的通知会先执行(所以给定的两个前置通知中,优先级高的那个会先执行)。 在“退出”连接点的情况下,最高优先级的通知会最后执行。(所以给定的两个后置通知中, 优先级高的那个会第二个执行)。当定义在不同的切面里的两个通知都需要在一个相同的连接点中运行, 那么除非你指定,否则执行的顺序是未知的。你可以通过指定优先级来控制执行顺序。 在标准的Spring方法中可以在切面类中实现org.springframework.core.Ordered 接口或者用Order注解做到这一点。在两个切面中, Ordered.getValue()方法返回值(或者注解值)较低的那个有更高的优先级。 当定义在相同的切面里的两个通知都需要在一个相同的连接点中运行, 执行的顺序是未知的(因为这里没有方法通过反射javac编译的类来获取声明顺序)。 考虑在每个切面类中按连接点压缩这些通知方法到一个通知方法,或者重构通知的片段到各自的切面类中 - 它能在切面级别进行排序。
      

  10.   


    对于我的情况,好像通知时作用在两个联结点上的吧:事务通知作用在a()上,a()所在的通知作用在c()上。后来看了一下Spring的官方参考文档:
    Advising aspects
    In Spring AOP, it is not possible to have aspects themselves be the target of advice from other
    aspects. The @Aspect annotation on a class s it as an aspect, and hence excludes it from
    auto-proxying.

    意思好像是说一个切面不能成为另一个切面的目标,即一个切面不能作用在另一个切面上。这个可能就是我的问题的原因:事务(一个切面)通知作用在MyAspectService(另一个切面)上,因此导致事务失效。这样的话又产生了两个问题:
    1.为什么执行c()的时候包围a()的事务是正常运行的呢?
    2.难道自定义切面中的方法是不能在事务中运行的?
      

  11.   

    楼主应该已经清楚,在没有配置事务的情况下,可能会出现no session的问题,这是无需置疑的;
    再往下说:
    结合楼主自己的分析,aop是不能再作用在aop上的
    其实楼主的就是一个切入点,虽然楼主定义了两个pointcut,但是他们的aspectj表达式是重叠的,所以他们会出现order的问题之所以a方法和c方法执行没有问题,是因为c在事务中,所以事务aop执行的时候,会产生session,然后放入当前线程中,这样之后的不在事务中的任何的aop或者其他的东西,都能在当前线程中获取该session,所以不会出现问题这也许恰好是事务aop先与你自定一的aop执行关于为何启动事务后就不会有no session问题,请看
    http://blog.csdn.net/Landor2004/archive/2008/12/18/3546267.aspx
    关于aop嵌套,可以看一下spring的开发参考手册  9.5.8. 通知事务操作  一节