Spring中事务管理的不同时机切入调用

我们经常遇到这样的场景,也许希望将某些调用放在事务提交前一刻才执行,避免因为业务出现异常到导致额外的修复现场的烦恼,也许会希望某些调用放在事务提交之后在执行,无论调用是否成功,都不影响事务的提交,也许我们希望无论事务是否提交或者回滚,都需要做一些工作来清理现场。如果我们使用了spring来管理事务,这些都是触手可及的梦想。

Sping里有一个类TransactionSynchronizationAdapter,它实现了TransactionSynchronization和Order接口,可以提供事务提交前后,完成前后,事务挂起等不同的业务切入时机。我们只要实现该类并按照需求重写对应的方法,将我们需要处理的业务植入就可以。

下面看我写的代码:

第一个类:TransactionAdditionalAdapter,对外提供方法注册,将业务数据绑定到threadLocal。

/**
 * 事务增强适配注册
 * @author tone
 *
 */
@Component(TransactionAdditionalAdapter.NAME)
public class TransactionAdditionalAdapter extends TransactionSynchronizationAdapter {
	public static final String NAME="transactionAdditionalAdapter";  
	private static final Log logger = LogFactory.getLog(TransactionAdditionalAdapter.class); 
    
    public void registBeforCommit(String billNo,String beanName,String method,Object... o){
    	regist(billNo,beanName,method,AdditionalCallData.BEFORE_COMMIT,o); 
    }
    
    public void registBeforCompletion(String billNo,String beanName,String method,Object... o){
    	regist(billNo,beanName,method,AdditionalCallData.BEFORE_COMPLETION,o); 
    }
    
    public void registAfterCommit(String billNo,String beanName,String method,Object... o){
    	regist(billNo,beanName,method,AdditionalCallData.AFTER_COMMIT,o); 
    }

    public void registAfterCompletion(String billNo,String beanName,String method,Object... o){
    	regist(billNo,beanName,method,AdditionalCallData.AFTER_COMPLETION,o); 
    }
 
    //注册要执行
    private void regist(String billNo,String beanName,String method,int point,Object... o){  
    	billNo=(billNo==null?"none":billNo);
    	if(beanName==null || beanName.isEmpty()||method==null||o==null){
    		logger.warn("单据号:"+billNo+"不注册事务增强处理,退出。");
    		return;
    	}  
    	String pointDsec="";
    	if(point==AdditionalCallData.BEFORE_COMMIT){
    		pointDsec="提交前";
    	}else if(point==AdditionalCallData.BEFORE_COMPLETION){
    		pointDsec="完成前";
    	}else if(point==AdditionalCallData.AFTER_COMMIT){
    		pointDsec="提交后";
    	}else if(point==AdditionalCallData.AFTER_COMPLETION){
    		pointDsec="完成后";
    	}else{
    		logger.error("point="+point+",切入点值非法");
    		return;
    	}
    	logger.debug("单据"+billNo+"注册事务"+pointDsec+"增强处理:"+beanName+"."+method);
    	AdditionalCallData additionalData=new AdditionalCallData(); 
    	additionalData.beanName=beanName;
        additionalData.method=method;
        additionalData.data=o;
        additionalData.callPoint=point; 
        additionalData.billNo=billNo;
        ThreadDataHelper.setThreadData(additionalData); 
    	TransactionSynchronizationManager.registerSynchronization(this); 
    }
    
    private Object[] getAdditionCall(int point){
    	List list=new LinkedList (); 
    	LinkedHashSet threadDatas = ThreadDataHelper.getThreadData(); 
    	if(!CollectionUtils.isEmpty(threadDatas)){
    		for(Object o:threadDatas){
    			if(o instanceof AdditionalCallData){
    				if(((AdditionalCallData)o).callPoint==point){
    					list.add(o);
    				}
    			}
    		}
    	} 
    	return list.toArray(); 
    }
    
    @Override
   	public void beforeCommit(boolean readOnly) {
    	logger.debug("事务提交前增强处理....");
    	executeTask(AdditionalCallData.BEFORE_COMMIT); 
   	}

   	@Override
   	public void beforeCompletion() {
   		logger.debug("事务完成前增强处理....");
   		executeTask(AdditionalCallData.BEFORE_COMPLETION); 
   	}
    
    @Override
    public void afterCommit(){
    	logger.debug("事务提交后增强处理....");
    	executeTask(AdditionalCallData.AFTER_COMMIT); 
    }
    
    @Override
    public void afterCompletion(int status) {
    	logger.debug("事务完成后增强处理....");
    	executeTask(AdditionalCallData.AFTER_COMPLETION); 
    	ThreadDataHelper.clear();
   }  
    
    private void executeTask(int point){
    	Object[] array = getAdditionCall(point);
    	if(ArrayUtils.isNotEmpty(array)){
    		for(int i=0;i<array.length;i++){
    			if(array[i] instanceof AdditionalCallData){
    				AdditionalCallData callData=(AdditionalCallData) array[i];  
    				if(callData.callPoint==AdditionalCallData.BEFORE_COMMIT){  //同步
    					Object service=SpringHelper.getBean(callData.beanName); 
    					try { 
    						Method m =CommonUtils.getMethod(service.getClass(), callData.method, callData.data);
    						logger.debug("单据号:"+callData.billNo+"执行[同步增强]处理:"+callData.beanName+"."+callData.method);
							m.invoke(service, callData.data);
						} catch (Exception e) {
							// TODO Auto-generated catch block
							logger.error("事务同步增强处理失败,服务:"+callData.beanName+"."+callData.method, e);
							ExceptionHelper.throwExcep("账务处理失败");
						}  
					}
    				else{  //异步
    					try { 
    						logger.debug("单据号:"+callData.billNo+"执行[异步增强]处理:"+callData.beanName+"."+callData.method);
    						Scheduler.getInstance().execute(callData.beanName,callData.method,0, callData.data);
    					} catch (Exception e) {
    						// TODO Auto-generated catch block
    						logger.error("事务异步增强处理失败,服务:"+callData.beanName+"."+callData.method, e);
    					}
    				}
    				
    			}
    		} 
    	}
    }
    
    
    class AdditionalCallData{
    	static final int BEFORE_COMMIT=1;
    	static final int BEFORE_COMPLETION=2;
    	static final int AFTER_COMMIT=3;
    	static final int AFTER_COMPLETION=4;
    	
    	int callPoint;
    	Object[] data;
    	String beanName;
    	String method;
    	String billNo;
    	
    	public AdditionalCallData(){
    		
    	}
    	
    	public AdditionalCallData(String beanName,String method,Object[] data,Integer callPoint){
    		this.beanName=beanName;
    		this.method=method;
    		this.data=data;
    		this.callPoint=callPoint;
    	}
    } 
}

第二个类:ThreadDataHelper,提供为线程绑定数据的功能,减少代码侵入,切入事务管理不同时机执行的代码从这里取数据执行。

  

/**
    * 使用该类,手动绑定线程数据
    * 
    * @author tone
    * @date 2016-12-29
    *
    */
public class ThreadDataHelper {
 
private static ThreadLocal<LinkedHashSet> _threadData = new ThreadLocal<LinkedHashSet>(); 

/**
* 设置数据
* @return
*/
public static void setThreadData(Object data){ 
LinkedHashSet datas = _threadData.get();
if(null==datas){
datas=new LinkedHashSet();
}
datas.add(data);
_threadData.set(datas);
}


/**
* 获取数据
* @return
*/
public static LinkedHashSet getThreadData(){
return _threadData.get();  
}
 
public static void clear(){
_threadData.set(null);
}

 
}



使用方法:

在业务代码里注册需要执行的业务代码即可,比如如果想在事务提交之前执行则调用registBeforCommit()方法:

transactionAdditionalAdapter.registBeforCommit(billNo,ProxyTransfer.NAME, "doFinms", accDatas); 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值