如下配置:
<!-- 配置事务详情 id表示事务通知名称 transaction-manager配置事务管理器 tx:attributes事务属性 tx:method事务应用的方法,相当于prop.key no-rollback-for配置遇到就提交的异常 rollback-for 配置遇到就混回滚的异常 --> <!-- more --> <tx:adviceid="TransactionId"transaction-manager="txManager"> <tx:attributes> <tx:methodname="*"propagation="REQUIRED"rollback-for="Throwable"/> </tx:attributes> </tx:advice> <aop:config> <!-- 此处如果是*.*那么会对dao层的方法也进行代理,从而无法实例化 ,所以注明只对service类进行切面点,如果该业务层有dao方法要注意排除dao方法--> <aop:advisoradvice-ref="TransactionId"pointcut="(execution(* Demo21AopTransaction.AccountServiceImpl.*(..) ))"/> <!-- &&(!execution(* Demo21AopTransaction.AccountServiceImpl.q(..))) --> </aop:config> <!-- 配置事务管理器,事务管理器是从连接池中取得连接的 --> <beanid="txManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <propertyname="dataSource"ref="dataSourceId"></property> </bean>
此时
@Test public void Demo()throws Exception{ ApplicationContext app = new ClassPathXmlApplicationContext("Demo21AopTransaction/applicationContext.xml"); AccountService service = (AccountService) app.getBean("accountServiceImpl"); // service.transfer("aaa", "bbb", 100); service.q(); service.a(); }
查看日志可以看到在执行service的a和q方法时,每一次方法的执行都进行了
申请数据库连接->得到数据库连接->开启事务->提交事务->归还连接
在一些不需要重用的业务逻辑中,完全可以将a和q方法变为一个方法执行,可以提高效率。
问题2
如下代码写法不正规,纯粹为了测试的方便,别效仿哦.
@Override public void transfer(final String inUser,final String outUser,final int money) throws Exception{ //该方法进行转账操作 accountDao.out(outUser, money); //int i=1/0;//模拟突发断电 accountDao.in(inUser, money); } @Override public void q() { // TODO Auto-generated method stub AccountService service = (AccountService) AopContext.currentProxy(); //获取代理对象 System.out.println("q开始"); try { service.transfer("aaa", "bbb", 200); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("断电"); int i =1 /0; //模拟发生了错误 抛出异常 System.out.println("q结束"); }
查看日志如下:
[QC] DEBUG [main] org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(367) | Creating new transaction with name [Demo21AopTransaction.AccountServiceImpl.q]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT,-Throwable [QC] DEBUG [main] org.springframework.jdbc.datasource.DataSourceTransactionManager.doBegin(206) | Acquired Connection [ProxyConnection[PooledConnection[com.mysql.jdbc.JDBC4Connection@34652b13]]] for JDBC transaction [QC] DEBUG [main] org.springframework.jdbc.datasource.DataSourceTransactionManager.doBegin(223) | Switching JDBC Connection [ProxyConnection[PooledConnection[com.mysql.jdbc.JDBC4Connection@34652b13]]] to manual commit q开始 [QC] DEBUG [main] org.springframework.transaction.support.AbstractPlatformTransactionManager.handleExistingTransaction(476) | Participating in existing transaction //1
只在q方法的开始之前开启了一次事务。在通过代理对象调用transfer方法的时候通过事务传播机制(required) transfer方法也共享了该事务。查看数据库,后续的’断电’.也确实引起了rollback。
但我测试发现 直接在q方法中调用transfer 而不通过代理对象调用transfer,q方法中的异常依旧会使transfer的结果回滚
按道理说直接调用的transfer并不会被代理。参考我的另一篇文章 java动态代理
@Override public void transfer(final String inUser,final String outUser,final int money) throws Exception{ //该方法进行转账操作 accountDao.out(outUser, money); //int i=1/0;//模拟突发断电 accountDao.in(inUser, money); } @Override public void q() { // TODO Auto-generated method stub System.out.println("q开始"); try { transfer("aaa", "bbb", 200); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("断电"); int i =1 /0; //模拟发生了错误 抛出异常 System.out.println("q结束"); }
对比两种情况下的日志输出。
后一种情况日志并无
[QC] DEBUG [main] org.springframework.transaction.support.AbstractPlatformTransactionManager.handleExistingTransaction(476) | Participating in existing transaction
可见是某种机制使得transfer方法并未被代理但q的事务仍旧有效