我们经常遇到这样的场景,也许希望将某些调用放在事务提交前一刻才执行,避免因为业务出现异常到导致额外的修复现场的烦恼,也许会希望某些调用放在事务提交之后在执行,无论调用是否成功,都不影响事务的提交,也许我们希望无论事务是否提交或者回滚,都需要做一些工作来清理现场。如果我们使用了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
/**
* 使用该类,手动绑定线程数据
*
* @author tone
* @date 2016-12-29
*
*/
public class ThreadDataHelper {
private static ThreadLocal _threadData = new ThreadLocal();
/**
* 设置数据
* @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);