我有一个 spring boot 项目,我在其中实施了责任链模式。我创建了多个服务类的链。 每个服务都在单例范围内。现在我有 2 个线程正在访问单例 bean,那么在这种情况下,一个线程所做的修改会影响在同一个 bean 上工作的另一个线程。我尝试按照要求制作 bean 范围,但它没有用。
TransactionChainContext - 定义链
@Service
public class TransactionChainContext {
@Autowired
TransactionChain validationChainOne;
@Autowired
TransactionChain validationChainTwo;
@Autowired
TransactionChain validationChainThree;
@Autowired
TransactionChain validationChainFour;
@Autowired
TransactionChain validationChainFive;
/* Execute by Thread 1 */
public void saveTransactionInChain(List<Transaction> txnList) {
validationChainOne.nextChain(validationChainTwo);
validationChainTwo.nextChain(validationChainThree);
validationChainThree.nextChain(validationChainFour);
validationChainFour.nextChain(null);
validationChainOne.processChain(txnList);
}
/* Execute by Thread 2 but it changes the chaing for thread 1 as well */
public void saveChainTwo(List<Transaction> txnList) {
validationChainOne.nextChain(validationChainFour);
validationChainFour.nextChain(validationChainFive);
validationChainFive.nextChain(null);
validationChainOne.processChain(txnList);
}
}
交易链接口
public interface TransactionChain {
void nextChain(TransactionChain transactionChain);
void processChain(List<Transaction> txnList);
}
编辑:验证类
@Service
public class ValidationChainOne implements TransactionChain {
TransactionChain nextTransactionChain;
@Override
public void nextChain(TransactionChain transactionChain) {
this.nextTransactionChain = transactionChain;
}
@Override
public void processChain(List<Transaction> txnList) {
// some business logic
//filtering txns bases on validations
if(nextTransactionChain !=null){
nextTransactionChain.processChain(txnList); // passing filtered txnList
}
}
}
我试过的
为了保证两个线程不互相影响,我把bean scope as request
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
但出现以下错误
'scopedTarget.ValidationChainOne': Scope 'request' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
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)
就我个人而言,我会从
nextChain(...)
中删除 TransactionChain
方法,因此它是不可变的,因此是线程安全的。然后你可以做类似的事情
public class ChainProcessor {
private TransactionChain[] steps;
public ChainProcessor(TransactionChain... steps) {
this.steps = steps;
}
public void process(ChainContext context) {
Arrays.stream(steps).forEach(step -> step.processChain(context));
}
}
/**
* This context class only required if you need to pass state from one step to another
*/
public class ChainContext {
public void setValue(String name, Object value) { ... }
public <T> T getValue(String name, Class<T> type) { ... }
}
public interface TransactionChain {
void processChain(ChainContext context);
}
@Service
public class TransactionChainContext {
@Autowired
TransactionChain validationChainOne;
@Autowired
TransactionChain validationChainTwo;
@Autowired
TransactionChain validationChainThree;
private ChainProcessor cp1;
private ChainProcessor cp2;
@PostConstruct
public void postConstruct() {
cp1 = new ChainProcessor(validationChainOne, validationChainTwo, validationChainThree);
cp2 = new ChainProcessor(validationChainThree, validationChainTwo, validationChainOne);
}
public void saveChainOne() {
cp1.process(new ChainContext());
}
public void saveChainTwo() {
cp2.process(new ChainContext());
}
}
我个人讨厌
@Autowired
和@PostConstruct
并且从不使用它们。我更喜欢最终成员变量和构造函数注入。请参阅这篇博文了解为什么字段注入是邪恶的
编辑
回答 Muddassir Rahman 关于过滤列表的问题
@Service
public class TransactionChainContext {
...
public void saveChainOne(List<Transaction> txnList) {
ChainContext context = new new ChainContext();
context.setValue("txnList", txnList);
cp1.process(context);
List<Transaction> filteredTxnList = context.getValue("txnList", List.class);
doStuff(filteredTxnList);
}
...
}
和
@Configuration
public class MyConfiguration {
@Bean
public TransactionChain validationChainOne() {
return context -> {
List<Transaction> txnList = context.getValue("txnList", List.class);
List<Transaction> filteredTxnList = txnList.stream().filter(t -> !t.getName().contains("foo")).collect(Collectors.toList());
context.setValue("txnList", filteredTxnList);
};
}
@Bean
public TransactionChain validationChainTwo() {
return context -> {
List<Transaction> txnList = context.getValue("txnList", List.class);
List<Transaction> filteredTxnList = txnList.stream().filter(t -> !t.getName().contains("bar")).collect(Collectors.toList());
context.setValue("txnList", filteredTxnList);
};
}
}
如果你想不那么通用,你可以有一个自定义的上下文类。例如
public interface ChainContext {
List<Transaction> getTransactions();
void addFilter(Predicate<Transaction> filter);
}