Aspect拦截方法,并支持事务后置处理

场景

需求中需要在联系人变更后动态地去触发联系人账号的注销和注册功能。
这里做法有两种:
1.逐一找到联系人变更的地方,在联系人update后添加该联系人账号的注销/注册逻辑,显然这种方式比较繁琐,且由于我们联系人变更有时候是需要走流程审批的(即联系人变更的入口比较多),所以此方法不好
2.利用aspect的代理拦截update方法,并对方法进行增强逻辑处理,织入账号注销/注册逻辑,达到效果

统一入口

第一步是找到联系人变更的统一入口,如果有必要可根据情况将update逻辑放入统一方法中。

@Override
	public void saveOrUpdateByBatch(List<SupplierContact> contactList, SupplierDaoQueryDTO daoQueryDTO) {
		if(!CollectionUtils.isEmpty(contactList)){
			for(SupplierContact contact:contactList){
				SupplierContactInfoEntity supplierContactInfoEntity = CopyBeanUtil.copyBean(contact, SupplierContactInfoEntity.class);
				if(null == supplierContactInfoEntity.getId()){
					baseMapper.insert(supplierContactInfoEntity);
				}else{
					baseMapper.updateById(supplierContactInfoEntity);
				}
			}
		}
		if(null != daoQueryDTO){
			baseMapper.deleteByNotExistIds(daoQueryDTO);
		}
	}

如上面的代码将修改和删除方法(新增、修改、删除都需要监听到,以便注销和注册)放到同一方法,然后对此方法进行拦截即可。

aspect拦截方法

public class SupplierContactSaveAspect {

    private SupplierContactRepostitory supplierContactRepostitory;
    private ApplicationContext applicationContext;

    @Pointcut(value = "execution( * com.huazhu.erp.supplierbc.infrastructure.persistence.repositoryimpl.SupplierContactInfoRepImpl.saveOrUpdateByBatch(java.util.List<com.huazhu.erp.supplierbc.domain.supplier.SupplierContact>,com.huazhu.erp.supplierbc.application.dto.SupplierDaoQueryDTO))&& args(contactList,daoQueryDTO)", argNames = "contactList,daoQueryDTO")
    public void pointcut(List<SupplierContact> contactList, SupplierDaoQueryDTO daoQueryDTO) {
        // should be empty by aspectj practice
    }

    @Around(value = "pointcut(contactList,daoQueryDTO)", argNames = "pjp,contactList,daoQueryDTO")
    public Object aroundUpdateContact(ProceedingJoinPoint pjp, List<SupplierContact> contactList,SupplierDaoQueryDTO daoQueryDTO) throws Throwable {
        String supplierNo;
        Set<String> newPhoneSet = new HashSet<>();
        if(CollectionUtils.isEmpty(contactList)){
            supplierNo = daoQueryDTO.getSupplierNo();
        }else {
            supplierNo = contactList.get(0).getSupplierNo();
            newPhoneSet = contactList.stream().map(SupplierContact::getPhone).collect(Collectors.toSet());
        }
        List<SupplierContact> oldSupplierContacts = supplierContactRepostitory.queryContactInfoBySupplierNo(supplierNo);
        Set<String> oldPhoneSet = oldSupplierContacts.stream().map(SupplierContact::getPhone).collect(Collectors.toSet());
        //修改的联系人也可能是需要逻辑删除(注销)的
        newPhoneSet.addAll(oldPhoneSet);
        //调用目标方法
        Object proceed = pjp.proceed();
        // 注册事务同步处理,如果有事务确保目标方法所在事务提交之后再执行后置操作
        if(TransactionSynchronizationManager.isSynchronizationActive()){
            TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
                @Override
                public void afterCommit() {
                    //执行后置操作
                    dealAdAccount(supplierNo, newPhoneSet);
                }
            });
        }else {
            dealAdAccount(supplierNo, newPhoneSet);
        }
	}
	/**
	     * 供应商变更事件处理ad账号
	     * 1.注销无效号码
	     * 2.开通有效账号
	     */
	    public void dealAdAccount(String supplierNo, Collection<String> newPhoneSet) {
	        if (StringUtils.isBlank(supplierNo)) {
	            return;
	        }
	        List<SupplierContact> supplierContactInfos = supplierContactRepostitory.queryContactInfoBySupplierNo(supplierNo);
	        SupplierContactUpdated supplierContactUpdated = new SupplierContactUpdated();
	        supplierContactUpdated.setSupplierNo(supplierNo);
	        supplierContactUpdated.setContactList(supplierContactInfos);
	        supplierContactUpdated.setNewPhoneList(newPhoneSet);
	        applicationContext.publishEvent(supplierContactUpdated);
	    }
	    
    }

需要注意上述关于事务的处理,因为切点方法可能处于某一事务当中,我们的逻辑是需要在事务提交后才能做后续的处理。
所以这里利用了TransactionSynchronizationAdapter.afterCommit()方法,确保执行的顺序性,然后异步执行dealAdAccount()方法。

关于事务提交后触发异步方法还有另外一种处理方法,即在异步方法dealAdAccount()中做事务限制。具体代码如下:

@Component
public class SupplierContactUpdatedListener {
    private final SupplierMemberUserService memberUserService;

    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT, fallbackExecution = true)
    @Async
    public void onApplicationEvent(SupplierContactUpdated supplierContactUpdated) {
        memberUserService.handleTermination(supplierContactUpdated.getNewPhoneList());
        memberUserService.batchRegister(supplierContactUpdated.getContactList());
    }
    
    @Autowired
    public SupplierContactUpdatedListener(SupplierMemberUserService memberUserService) {
        this.memberUserService = memberUserService;
    }
}

利用@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT, fallbackExecution = true)注解,保证所监听方法的事务提交后再进行其他操作,这种方式的好处是不需要在切面拦截中加入TransactionSynchronizationAdapter.afterCommit()方法,最大程度的降低对切入点的影响,将事务的处理放到异步方法dealAdAccount()中。

码字不易,如有帮助,还请点赞支持!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值