Transactional钩子函数实现 [ 自定义注解 + 多线程 ]:
-
适用对象:在有事务的代码块中,想在事务提交之后执行操作的业务环境中,一般用户redis缓存,发送mq消息等
-
优点:傻瓜式操作,注解式操作,两种形式集成
-
缺点:剥离主线程,执行方法无事务
代码结构:
- annotation
- AfterCommit [事务提交后置执行注解]
- BeforeCommit [事务提交前置执行注解]
- aspect
- TransactionalAop [事务提交AOP]
- thread
- ThreadUtil [线程池配置类]
- TransactionalExecutor [事务执行器]
AfterCommit:
import org.springframework.core.annotation.Order;
import java.lang.annotation.*;
/**
* 事务提交后置执行
*
* @author a-xin
* @date 2019/3/12 23:50
*/
@Order(2)
@Inherited
@Target(ElementType.METHOD)
@Retention(value = RetentionPolicy.RUNTIME)
public @interface AfterCommit {
/**
* 事务提交后执行方法名称
*/
String method();
}
BeforeCommit:
import org.springframework.core.annotation.Order;
import java.lang.annotation.*;
/**
* 事务提交前置执行
*
* @author a-xin
* @date 2019/3/12 23:50
*/
@Order(1)
@Inherited
@Target(ElementType.METHOD)
@Retention(value = RetentionPolicy.RUNTIME)
public @interface BeforeCommit {
/**
* 事务提交前执行方法名称
*/
String method();
}
TransactionalAop:
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.CharSequenceUtil;
import com.axin229913.thread.annotation.AfterCommit;
import com.axin229913.thread.annotation.BeforeCommit;
import com.axin229913.thread.util.TransactionalExecutor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@Component
@Aspect
@EnableAspectJAutoProxy
public class TransactionalAop {
@Resource
private TransactionalExecutor transactionalExecutor;
/**
* 当前线程子线程
*/
private static final ThreadLocal<Map<String, Boolean>> THREAD_LOCAL = new ThreadLocal<>();
private static final Map<String, Boolean> COMMIT_MAP = new HashMap<>();
@PostConstruct
private void init() {
COMMIT_MAP.put(TransactionalExecutor.BEFORE_COMMIT, Boolean.TRUE);
COMMIT_MAP.put(TransactionalExecutor.AFTER_COMMIT, Boolean.TRUE);
}
@Pointcut("@annotation(com.axin229913.thread.annotation.AfterCommit)")
public void pointCutAfter() {
}
@Pointcut("@annotation(com.axin229913.thread.annotation.BeforeCommit)")
public void pointCutBefore() {
}
@Before(value = "pointCutAfter()")
public void afterPointCut() {
if (CollUtil.isEmpty(THREAD_LOCAL.get())) {
THREAD_LOCAL.set(COMMIT_MAP);
}
}
@AfterThrowing(value = "pointCutAfter()")
public void afterThrowing1() {
THREAD_LOCAL.get().put(TransactionalExecutor.AFTER_COMMIT, Boolean.FALSE);
}
@Before(value = "pointCutBefore()")
public void beforePointCut() {
if (CollUtil.isEmpty(THREAD_LOCAL.get())) {
THREAD_LOCAL.set(COMMIT_MAP);
}
}
@AfterThrowing(value = "pointCutBefore()")
public void afterThrowing2() {
THREAD_LOCAL.get().put(TransactionalExecutor.BEFORE_COMMIT, Boolean.FALSE);
}
@After("pointCutAfter()")
public void afterPointCut(JoinPoint joinPoint) {
if (THREAD_LOCAL.get().get(TransactionalExecutor.AFTER_COMMIT)) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
if (!method.isAnnotationPresent(AfterCommit.class)) {
return;
}
AfterCommit afterCommit = method.getAnnotation(AfterCommit.class);
String methodName = afterCommit.method();
if (CharSequenceUtil.isBlank(methodName)) {
return;
}
Class<?> declaringClass = method.getDeclaringClass();
transactionalExecutor.afterRun(() -> {
try {
Method declaredMethod = declaringClass.getDeclaredMethod(methodName);
declaredMethod.invoke(joinPoint.getTarget());
} catch (Throwable e) {
throw new RuntimeException(e);
}
});
}
}
@After("pointCutBefore()")
public void beforePointCut(JoinPoint joinPoint) {
if (THREAD_LOCAL.get().get(TransactionalExecutor.BEFORE_COMMIT)) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
if (!method.isAnnotationPresent(BeforeCommit.class)) {
return;
}
BeforeCommit beforeCommit = method.getAnnotation(BeforeCommit.class);
String methodName = beforeCommit.method();
if (CharSequenceUtil.isBlank(methodName)) {
return;
}
Class<?> declaringClass = method.getDeclaringClass();
transactionalExecutor.beforeRun(() -> {
try {
Method declaredMethod = declaringClass.getDeclaredMethod(methodName);
declaredMethod.invoke(joinPoint.getTarget());
} catch (Throwable e) {
throw new RuntimeException(e);
}
});
}
}
}
ThreadUtil:
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* 类名:动态线程池工具类,按照服务器资源cpu核数定义
*
* @author a-xin
* @date 15:17
*/
@Slf4j
@Configuration
public class ThreadUtil {
@Value("${spring.application.name}")
private String SERVICE_NAME;
/**
* CPU核心数
*/
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
/**
* 核心线程数
*/
private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
/**
* 最大线程数
*/
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
/**
* 空闲线程存活时间
*/
private static final int KEEP_ALIVE = 60;
/**
* 任务队列
*/
private static final int WORK_QUEUE = MAXIMUM_POOL_SIZE * 2;
/**
* 自定义线程池<br/>
* corePoolSize - 即使空闲时仍保留在池中的线程数,除非设置allowCoreThreadTimeOut<br/>
* maximumPoolSize - 池中允许的最大线程数<br/>
* keepAliveTime - 当线程数大于内核时,这是多余的空闲线程在终止前等待新任务的最大时间<br/>
* unit - keepAliveTime参数的时间单位<br/>
* workQueue - 用于在执行任务之前使用的队列,这个队列将仅保存execute方法提交的Runnable任务<br/>
* threadFactory - 执行程序创建新线程时使用的工厂<br/>
* handler - 执行被阻止时使用的处理程序,因为达到线程限制和队列容量<br/>
* <p>
* RejectedExecutionHandler rejected = null;<br/>
* - rejected = new ThreadPoolExecutor.AbortPolicy();//默认,队列满了丢任务,抛出异常<br/>
* - rejected = new ThreadPoolExecutor.DiscardPolicy();//队列满了丢任务,不抛出异常【如果允许任务丢失这是最好的】<br/>
* - rejected = new ThreadPoolExecutor.DiscardOldestPolicy();//将最早进入队列的任务删,之后再尝试加入队列<br/>
* - rejected = new ThreadPoolExecutor.CallerRunsPolicy();//如果添加到线程池失败,那么主线程会自己去执行该任务,回退
*/
@Bean(value = "thread_pool")
public ThreadPoolExecutor THREAD_POOL() {
log.info("===>>> Start initializing the thread pool configuration:serviceName:{}, corePoolSize:{}, maximumPoolSize:{}, keepAlive:{} second",
SERVICE_NAME,
CORE_POOL_SIZE,
MAXIMUM_POOL_SIZE,
KEEP_ALIVE);
return new ThreadPoolExecutor(
CORE_POOL_SIZE,
MAXIMUM_POOL_SIZE,
KEEP_ALIVE,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(WORK_QUEUE),
r -> new Thread(r, "[" + SERVICE_NAME + "]-threadPool-" + r.hashCode()),
new ThreadPoolExecutor.DiscardOldestPolicy());
}
}
TransactionalExecutor:
import cn.hutool.core.collection.CollUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ThreadPoolExecutor;
/**
* 类名:事务处理线程类
*
* @author a-xin
* @date 2024/4/29 13:57
*/
@Slf4j
@Component
public class TransactionalExecutor implements TransactionSynchronization {
public static final String BEFORE_COMMIT = "BEFORE_COMMIT";
public static final String AFTER_COMMIT = "AFTER_COMMIT";
private static final ThreadLocal<Map<String, Runnable>> RUNNABLE = new ThreadLocal<>();
@Resource
private ThreadUtil threadUtil;
private ThreadPoolExecutor THREAD_POOL;
@PostConstruct
public void init() {
log.info("===>>> Start initializing the transactional thread pool...");
THREAD_POOL = threadUtil.THREAD_POOL();
}
@PreDestroy
public void destroy() {
log.error("Start destroy the transactional thread pool...");
if (null != THREAD_POOL && !THREAD_POOL.isShutdown()) {
THREAD_POOL.shutdown();
}
}
/**
* 事务提交执行,如果当前执行方法存在事务,则会通过{@link TransactionalExecutor}的afterCommit()方法进行执行,执行条件为当前事务正常提交<br/>
* 如果当前执行方法不存在事务,则直接执行runnable方法<br/>
* 请注意:此方法为自定义线程池异步执行,执行过程中产生的异常,在子线程中是不能返回主线程的!!!
*
* @param runnable the runnable task
*/
public void beforeRun(@NonNull Runnable runnable) {
if (!TransactionSynchronizationManager.isSynchronizationActive()) {
log.warn("There are no transactions for the current method...");
runnable.run();
return;
}
log.info("There is a transaction for the current method...");
Map<String, Runnable> runnableMap = RUNNABLE.get();
if (CollUtil.isEmpty(runnableMap)) {
runnableMap = new HashMap<>();
RUNNABLE.set(runnableMap);
TransactionSynchronizationManager.registerSynchronization(this);
}
runnableMap.put(BEFORE_COMMIT, runnable);
}
/**
* 事务提交执行,如果当前执行方法存在事务,则会通过{@link TransactionalExecutor}的afterCommit()方法进行执行,执行条件为当前事务正常提交<br/>
* 如果当前执行方法不存在事务,则直接执行runnable方法<br/>
* 请注意:此方法为自定义线程池异步执行,执行过程中产生的异常,在子线程中是不能返回主线程的!!!
*
* @param runnable the runnable task
*/
public void afterRun(@NonNull Runnable runnable) {
if (!TransactionSynchronizationManager.isSynchronizationActive()) {
log.warn("There are no transactions for the current method...");
runnable.run();
return;
}
log.info("There is a transaction for the current method...");
Map<String, Runnable> runnableMap = RUNNABLE.get();
if (CollUtil.isEmpty(runnableMap)) {
runnableMap = new HashMap<>();
RUNNABLE.set(runnableMap);
TransactionSynchronizationManager.registerSynchronization(this);
}
runnableMap.put(AFTER_COMMIT, runnable);
}
@Override
public void beforeCommit(boolean readOnly) {
log.debug("The transaction commit is processed... ");
Map<String, Runnable> runnableMap = RUNNABLE.get();
THREAD_POOL.execute(runnableMap.get(BEFORE_COMMIT));
}
@Override
public void afterCommit() {
log.debug("The transaction commit is processed... ");
Map<String, Runnable> runnableMap = RUNNABLE.get();
THREAD_POOL.execute(runnableMap.get(AFTER_COMMIT));
}
@Override
public void afterCompletion(int status) {
log.info("The current transaction has been processed: {} ... ", status);
RUNNABLE.remove();
}
}
使用实例:
/**
* 测试API接口
*/
@Override
@Transactional(rollbackFor = Exception.class)
@AfterCommit(method = "after")
@BeforeCommit(method = "before")
public Result<String> testApi(SysForm form) {
SysUserInfo loginUser = UserUtil.getLoginUser();
loginUser.setEmail("909090@qq.com");
sysUserInfoMapper.updateById(loginUser);
System.out.println(1 / 0);
transactionalExecutor.afterRun(() -> {
System.out.println("after: " + loginUser);
});
transactionalExecutor.beforeRun(() -> {
System.out.println("before: " + loginUser);
});
return Result.success();
}
public void after() {
log.error("后...");
log.error("hou...");
}
public void before() {
log.error("前...");
log.error("qian...");
}
第一种类型:使用注解式:
第二种类型:使用线程式:
运行结果:
如有问题,可联系:QQxin7045