当我们有业务需要在事务提交过后进行某一项或者某一系列的业务操作时候我们就可以使用TransactionSynchronizationManager

通过spring的aop机制将需要进行后置业务处理的操作,提交给spring的处理机制,并且切入到事务处理的后面

TransactionSynchronizationManager这个类中由一系列的ThreadLocal ,我们需要关注的是synchronizations,在后面使用到的TransactionSynchronizationManager.isSynchronizationActive()、TransactionSynchronizationManager.registerSynchronization()和new TransactionSynchronizationAdapter(),都与它密切有关。

在Spring在开启数据库事务(无论是使用@Transactional注解,还是用xml配置)时,都会向其中写入一个实例,用于自动处理Connection的获取、提交或回滚等操作。

再看isSynchronizationActive()方法,判断了synchronizations中是否有数据(Set<TransactionSynchronization>非null即可,并不要求其中有TransactionSynchronization实例。

再看registerSynchronization()方法,首先调用isSynchronizationActive()做一个校验;然后将入参synchronization添加到synchronizations 中。入参synchronization中的方法不会在这里执行,而是要等到事务执行到特定阶段时才会被调用。

TransactionSynchronizationAdapter是一个适配器:它实现了TransactionSynchronization接口,并为每一个接口方法提供了一个空的实现。这类适配器的基本思想是:接口中定义了很多方法,然而业务代码往往只需要实现其中一小部分。利用这种“空实现”适配器,我们可以专注于业务上需要处理的回调方法,而不用在业务类中放大量而且重复的空方法。

结合TransactionSynchronizationManager和TransactionSynchronizationAdapter利用ThreadPoolExecutor实现一个事务后多线程处理功能。

package com.*.module.spring.support;

import com.google.common.util.concurrent.ThreadFactoryBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.support.TransactionSynchronizationAdapter;
import org.springframework.transaction.support.TransactionSynchronizationManager; import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*; /**
* 事务提交异步线程
*
* @author ly
*/
public class TransactionAfterCommitExecutor extends ThreadPoolExecutor { private static final Logger LOGGER = LoggerFactory.getLogger(TransactionAfterCommitExecutor.class); public TransactionAfterCommitExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
} public TransactionAfterCommitExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
} public TransactionAfterCommitExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);
} public TransactionAfterCommitExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
} private ThreadLocal<List<Runnable>> currentRunables = new ThreadLocal<List<Runnable>>(){
@Override
protected List<Runnable> initialValue() {
return new ArrayList<>(5);
}
}; private ThreadLocal<Boolean> registed = new ThreadLocal<Boolean>(){
@Override
protected Boolean initialValue() {
return false;
}
}; /**
* 默认策略丢弃最老的数据
*/
public TransactionAfterCommitExecutor() {
this(
50, 500,
500L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(1024),
new ThreadFactoryBuilder().setNameFormat("transaction-after-commit-call-pool-%d").build(),
new ThreadPoolExecutor.DiscardOldestPolicy());
} @Override
public void execute(final Runnable runnable) {
//如果事务同步未启用则认为事务已经提交,马上进行异步处理
if (!TransactionSynchronizationManager.isSynchronizationActive()) {
super.execute(runnable);
} else {
//同一个事务的合并到一起处理
currentRunables.get().add(runnable);
//如果存在事务则在事务结束后异步处理
if(!registed.get()){
TransactionSynchronizationManager.registerSynchronization(new AfterCommitTransactionSynchronizationAdapter());
registed.set(true);
}
}
} @Override
public Future<?> submit(final Runnable runnable) {
//如果事务同步未启用则认为事务已经提交,马上进行异步处理
if (!TransactionSynchronizationManager.isSynchronizationActive()) {
return super.submit(runnable);
} else {
final RunnableFuture<Void> ftask = newTaskFor(runnable, null);
//如果存在事务则在事务结束后异步处理
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
@Override
public void afterCommit() {
TransactionAfterCommitExecutor.super.submit(ftask);
}
});
return ftask;
}
} private class AfterCommitTransactionSynchronizationAdapter extends TransactionSynchronizationAdapter{
@Override
public void afterCompletion(int status) {
final List<Runnable> txRunables = new ArrayList<>(currentRunables.get());
currentRunables.remove();
registed.remove();
if(status == STATUS_COMMITTED){
TransactionAfterCommitExecutor.super.execute(new Runnable() {
@Override
public void run() {
for (Runnable runnable : txRunables) {
try {
runnable.run();
} catch (Exception e) {
LOGGER.error("ex:",e);
}
}
}
});
}
}
}
}
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.transaction.support.TransactionSynchronizationAdapter; @Transactional(readOnly = false,propagation=Propagation.REQUIRED)//开事物
public void save(String name,Integer age ,BigDecimal amount){
Zexample1Model zexample1Model = new Zexample1Model();
zexample1Model.setName(name+"_");
zexample1Model.setAge(age);
zexample1Model.setAmount(amount);
zexample1Model.setAddTime(new Date());
zexample1Model.setStatus(1);
zexample1Dao.save(zexample1Model);
System.out.println("id="+zexample1Model.getId()); TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
@Override
public void afterCommit() {
System.out.println("send email after transaction commit...");
}
});
System.out.println("this method complete....");
}

或者用于切面的事务处理

package com.my.data.aop;

import java.lang.reflect.Field;
import java.util.Objects; import com.my.data.multisource.redismanager.RedisBean;
import com.my.data.utils.ThreadLocalUtil;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.core.RedisConnectionUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.transaction.support.TransactionSynchronizationManager; import javax.annotation.Resource; @Aspect
@Component
public class RedisAspect { private Logger logger = LogManager.getLogger(RedisAspect.class); /**
* 定义切入点,切入点为com.example.aop下的所有函数
*/
@Pointcut("execution(public * com.my.data.service..*.*(..))")
public void redisPointcut() {
} @Resource(name = RedisBean.defaultStringRedis)
private StringRedisTemplate redis; /**
* 前置通知:在连接点之前执行的通知
*
* @param joinPoint
* @throws Throwable
*/
@Before("redisPointcut()")
public void doBefore(JoinPoint joinPoint) throws Throwable {
try {
Field field = joinPoint.getTarget().getClass().getDeclaredField("redis");
field.setAccessible(true);
Object targetRedis = field.get(joinPoint.getTarget());
if (!Objects.isNull(targetRedis)) {
this.redis = (StringRedisTemplate) targetRedis;
logger.info("redis : {}", redis.hashCode());
}
}catch (NoSuchFieldException e) {
logger.info("not found redis");
}catch (Exception e) {
logger.error("doAfterReturning error.", e);
}
} @AfterReturning(returning = "ret", pointcut = "redisPointcut()")
public void doAfterReturning(Object ret) throws Throwable {
try {
if (!Objects.isNull(redis)) {
logger.info("redis : {}", redis.hashCode()); Object bindResource = TransactionSynchronizationManager.getResource(redis.getConnectionFactory());
if(null == bindResource) {
RedisConnectionUtils.unbindConnection(redis.getConnectionFactory());
} }
} catch (Exception e) {
logger.error("doAfterReturning error.", e);
}
} }

TransactionSynchronizationManager用法和含义(转)的更多相关文章

  1. java中.currentTimeMillis的用法和含义

    用法:可以用法获取当前时间的毫秒数,可以通过毫秒数进行时间比较,时间转化以及时间格式化等.public class SystemTime {public static void main(String ...

  2. js中的attributes和Attribute的用法和区别。

    一:Attribute的几种用法和含义(attributes和Attribute都是用来操作属性的) getAttribute:获取某一个属性的值: setAttribute:建立一个属性,并同时给属 ...

  3. IBatis 2.x 和 MyBatis 3.0.x 的区别(从 iBatis 到 MyBatis)

    从 iBatis 到 MyBatis,你准备好了吗? 对于从事 Java EE 的开发人员来说,iBatis 是一个再熟悉不过的持久层框架了,在 Hibernate.JPA 这样的一站式对象 / 关系 ...

  4. 【Html 学习笔记】第二节——文本格式

    上一节基本已经了解了一些html的基础,这一节主要学习html处理文本相关内容,直接看内容吧. 字体: 预格式文本:<pre> 地址:<address> 缩写:<abbr ...

  5. Java陷阱之assert关键字

    Java陷阱之assert关键字   一.概述   在C和C++语言中都有assert关键,表示断言. 在Java中,同样也有assert关键字,表示断言,用法和含义都差不多.   二.语法   在J ...

  6. C++读写文件ofstream,ifstream,fstream)[转]

    在看C++编程思想中,每个练习基本都是使用ofstream,ifstream,fstream,以前粗略知道其用法和含义,在看了几位大牛的博文后,进行整理和总结: 这里主要是讨论fstream的内容:[ ...

  7. Makefile规则③规则语法、依赖、通配符、目录搜寻、目标

    规则语法 通常规则的语法格式如下: TARGETS : PREREQUISITES COMMAND ... 或者: TARGETS : PREREQUISITES ; COMMAND COMMAND ...

  8. Android 手写Binder 教你理解android中的进程间通信

    关于Binder,我就不解释的太多了,网上一搜资料一堆,但是估计还是很多人理解的有困难.今天就教你如何从 app层面来理解好Binder. 其实就从我们普通app开发者的角度来看,仅仅对于androi ...

  9. mybatis注解详解

    首 先当然得下载mybatis-3.0.5.jar和mybatis-spring-1.0.1.jar两个JAR包,并放在WEB-INF的lib目录下 (如果你使用maven,则jar会根据你的pom配 ...

随机推荐

  1. 路由器04--OPKG

    1.简介 https://oldwiki.archive.openwrt.org/doc/techref/opkg Opkg 是一个基于 ipkg 的轻量级的软件包管理系统,主要用于嵌入式系统,目前应 ...

  2. 《Mysql - SQL优化》

    一:在查询语句时,应该注意的优化问题 - SELECT语句务必指明字段名称 - SELECT * 会增加很多不必要的消耗(CPU.IO.内存.网络带宽) - 同时会让 Mysql 优化器无法优化 -  ...

  3. XDebug调试

    安装 访问Xdebug 点击download 找到RELEASES,点击 custom installation instructions. 在白色框框内填入phpinfo()出来的源码 点击Anal ...

  4. echarts配置项说明//持续添加

    <template> <div>      <!-- <h2>本月抄表完成率</h2> --> <!-- <div id=&qu ...

  5. CSS之cursor用法

    cursor: url('~ROOT/shared/assets/image/vn-text-cursor-31-49.png') 22 22, nw-resize; 另外还有一个 cursor: g ...

  6. 为什么用JS取不到cookie的值?解决方法如下!

    注意:cookie是基于域名来储存的.要放到测试服务器上或者本地localhost服务器上才会生效.cookie具有不同域名下储存不可共享的特性.单纯的本地一个html页面打开是无效的. 明明在浏览中 ...

  7. Sonya and Bitwise OR CodeForces - 1004F (线段树,分治)

    大意: 给定序列$a$, 给定整数$x$. 两种操作(1)单点修改 (2)给定区间$[l,r]$,求有多少子区间满足位或和不少于$x$. 假设不带修改. 固定右端点, 合法区间关于左端点单调的. 可以 ...

  8. Asp.net core 学习笔记 ( ef core transaction scope & change level )

    ef core 有 unit of work 的概念,当我们 save change 时会自动使用 transaction 确保更新的一致性. 隔离级别是默认的 read committed 不允许脏 ...

  9. 在论坛中出现的比较难的sql问题:9(触发器专题 插入数据自动更新表数据)

    原文:在论坛中出现的比较难的sql问题:9(触发器专题 插入数据自动更新表数据) 最近,在论坛中,遇到了不少比较难的sql问题,虽然自己都能解决,但发现过几天后,就记不起来了,也忘记解决的方法了. 所 ...

  10. 手机如何修改host文件

    常常在想本地开发的时候使用pc端测试移动端,由于有安全域名限制,因此在本地修改了机器的host.将域名映射到本地局域网.这个做法是很正常的也很常见.但是现在新的需求是我需要在手机上也能够正常测试啊!怎 ...