Spring Boot单例Bean责任链多线程冲突及请求域尝试失败求助
在Spring Boot项目中实现责任链模式,由多个Service类组成责任链,每个Service默认是Singleton作用域。当两个线程访问这些单例Bean时,一个线程对责任链的修改会干扰另一个线程的执行。尝试将Bean作用域改为request,但未成功。
代码实现
TransactionChainContext(责任链定义)
@Service public class TransactionChainContext { @Autowired TransactionChain validationChainOne; @Autowired TransactionChain validationChainTwo; @Autowired TransactionChain validationChainThree; @Autowired TransactionChain validationChainFour; @Autowired TransactionChain validationChainFive; /* 线程1执行 */ public void saveTransactionInChain(List<Transaction> txnList) { validationChainOne.nextChain(validationChainTwo); validationChainTwo.nextChain(validationChainThree); validationChainThree.nextChain(validationChainFour); validationChainFour.nextChain(null); validationChainOne.processChain(txnList); } /* 线程2执行,但会修改线程1的责任链 */ public void saveChainTwo(List<Transaction> txnList) { validationChainOne.nextChain(validationChainFour); validationChainFour.nextChain(validationChainFive); validationChainFive.nextChain(null); validationChainOne.processChain(txnList); } }
TransactionChain 接口
public interface TransactionChain { void nextChain(TransactionChain transactionChain); void processChain(List<Transaction> txnList); }
验证类示例(ValidationChainOne)
@Service public class ValidationChainOne implements TransactionChain { TransactionChain nextTransactionChain; @Override public void nextChain(TransactionChain transactionChain) { this.nextTransactionChain = transactionChain; } @Override public void processChain(List<Transaction> txnList) { // 业务逻辑:基于验证过滤交易记录 if(nextTransactionChain != null){ nextTransactionChain.processChain(txnList); // 传递过滤后的交易列表 } } }
已尝试方案及错误
为保证线程隔离,将Bean作用域改为request:
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
执行时出现以下错误:
'scopedTarget.ValidationChainOne': 请求作用域(request)当前线程未激活;如果要从单例Bean引用该Bean,请考虑为其定义作用域代理;嵌套异常为java.lang.IllegalStateException: 未找到线程绑定的请求:你是否在实际Web请求之外引用了请求属性,或者在原始接收线程之外处理请求?如果确实在Web请求内操作仍收到此消息,你的代码可能运行在DispatcherServlet之外:这种情况下,请使用RequestContextListener或RequestContextFilter暴露当前请求。
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:368)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
at org.springframework.aop.target.SimpleBeanTargetSource.getTarget(SimpleBeanTargetSource.java:35)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:673)
解决方案
方案1:使用Prototype作用域
将所有TransactionChain实现类的作用域改为prototype,这样每次从容器获取时都会创建新实例,避免线程间状态共享:
@Service @Scope(value = "prototype") public class ValidationChainOne implements TransactionChain { // 原有代码不变 }
同时在TransactionChainContext中,每次构建责任链时通过ApplicationContext获取新的实例:
@Service public class TransactionChainContext { @Autowired private ApplicationContext applicationContext; /* 线程1执行 */ public void saveTransactionInChain(List<Transaction> txnList) { ValidationChainOne chainOne = applicationContext.getBean(ValidationChainOne.class); ValidationChainTwo chainTwo = applicationContext.getBean(ValidationChainTwo.class); ValidationChainThree chainThree = applicationContext.getBean(ValidationChainThree.class); ValidationChainFour chainFour = applicationContext.getBean(ValidationChainFour.class); chainOne.nextChain(chainTwo); chainTwo.nextChain(chainThree); chainThree.nextChain(chainFour); chainFour.nextChain(null); chainOne.processChain(txnList); } /* 线程2执行 */ public void saveChainTwo(List<Transaction> txnList) { ValidationChainOne chainOne = applicationContext.getBean(ValidationChainOne.class); ValidationChainFour chainFour = applicationContext.getBean(ValidationChainFour.class); ValidationChainFive chainFive = applicationContext.getBean(ValidationChainFive.class); chainOne.nextChain(chainFour); chainFour.nextChain(chainFive); chainFive.nextChain(null); chainOne.processChain(txnList); } }
方案2:重构责任链,移除实例状态
将责任链的下一个节点从实例变量改为方法参数,让每个processChain调用时传递下一个节点,彻底避免实例状态共享:
// 修改TransactionChain接口 public interface TransactionChain { void processChain(List<Transaction> txnList, TransactionChain nextChain); } // 实现类示例 @Service public class ValidationChainOne implements TransactionChain { @Override public void processChain(List<Transaction> txnList, TransactionChain nextChain) { // 业务逻辑:基于验证过滤交易记录 if(nextChain != null){ nextChain.processChain(txnList, null); // 传递下一个节点(如果有后续节点可继续传递) } } } // TransactionChainContext调整 @Service public class TransactionChainContext { @Autowired TransactionChain validationChainOne; @Autowired TransactionChain validationChainTwo; @Autowired TransactionChain validationChainThree; @Autowired TransactionChain validationChainFour; @Autowired TransactionChain validationChainFive; public void saveTransactionInChain(List<Transaction> txnList) { validationChainOne.processChain(txnList, validationChainTwo); validationChainTwo.processChain(txnList, validationChainThree); validationChainThree.processChain(txnList, validationChainFour); validationChainFour.processChain(txnList, null); } public void saveChainTwo(List<Transaction> txnList) { validationChainOne.processChain(txnList, validationChainFour); validationChainFour.processChain(txnList, validationChainFive); validationChainFive.processChain(txnList, null); } }
方案3:使用ThreadLocal存储线程私有状态
如果需要保留现有接口结构,可以用ThreadLocal存储每个线程的责任链节点状态,确保线程隔离:
@Service public class ValidationChainOne implements TransactionChain { private ThreadLocal<TransactionChain> nextTransactionChain = new ThreadLocal<>(); @Override public void nextChain(TransactionChain transactionChain) { nextTransactionChain.set(transactionChain); } @Override public void processChain(List<Transaction> txnList) { // 业务逻辑 TransactionChain next = nextTransactionChain.get(); if(next != null){ next.processChain(txnList); } // 处理完成后清理ThreadLocal,避免内存泄漏 nextTransactionChain.remove(); } }
内容的提问来源于stack exchange,提问作者Muddassir Rahman

