用AOP实现业务service的重新调用(三)

承接 用AOP实现业务service的重新调用(二),我们继续......

代码看似不多,但实现上需要考虑很多问题,因为哪怕只有一个问题没搞定,整个实现就是失败的.

问题列表:
1>事务完整性的问题

       前后两次,是否能保证事务的完整性,我们的事务正好也是通过spring的aop实现的,所以要注意我们新加的ServiceRetryAdvice拦截器要在spring事务拦截器之前调用,也就是包在事务拦截器外面,这样才不会影响spring的事务提交/回滚机制.

<value>serviceRetryInterceptor</value>
<value>transactionInterceptor</value>
<bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor">
		<property name="transactionManager"><ref bean="transactionManager"/></property>
		<property name="transactionAttributeSource"><ref bean="txAttribute"/></property>
	</bean>
	
	<bean id="transactionManagerAutoProxy" 
			class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
		<property name="interceptorNames">
			<list>
				<value>serviceRetryInterceptor</value>
				<value>transactionInterceptor</value>
			</list>
		</property>
		<property name="beanNames">
			<list>
				<value>testRootService</value>
				<value>testService</value>
			</list>
		</property>
	</bean>
2>事务传播行为PROPAGATION

      通常最简单的情况是一个service方法对应一个事务,按照前面的配置,这种情况是没有问题的,前一次调用失败后,重新调用service,spring会重新开始一个事务,所以新事务会获取一个新的连接,执行成功返回前端.

       

       还有一种情况是service方法里面又调用了子service方法,我们遇到的情况用的是PROPAGATION_REQUIRED(子service方法里面判断当前线程是否存在transaction,如果存在就复用,不存在再创建新transaction),所以这里仅讨论PROPAGATION_REQUIRED的情况,其它情况可以自己去详细考证,如果exception是发生在父service方法里面,没有问题.如果发生在子service方法里面,我们重调的是子service方法,这种情况下retry调用的时候,因为transaction还在,所以继续复用原来的transaction可是transaction里面的connection还是原来的无效连接,所以retry仍然会百分之百失败,所以这种case我们无能为力,直接把异常抛给前端.因为事务的commit/rollback只在父service方法里面进行,所以不会破坏事务的完整性.

 
3>多线程的问题(retryNum)

       为了提高效率,spring内部对interceptor进行了cache,同一个service class+method,会共用同一个interceptor,所以多线程环境下,retryNum成员变量要保证线程安全,解决方法是:我们用了ThreadLocal<Integer>,这样就不用担心多线程访问的问题了.

4>ThreadLocal的注意事项

关于ThreadLocal,还需要注意内存泄露的问题,因为ThreadLocal内部对每一个线程都留了一个map,所以我们在使用完以后要主动remove掉里面的内容.

} finally {
			retryNum.remove();
}

猜你喜欢

转载自blogzhoubo.iteye.com/blog/2358305