单例作用域 bean 的问题以防万一

问题描述 投票:0回答:1

我有一个 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)
java spring multithreading singleton
1个回答
1
投票

就我个人而言,我会从

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);  
}
© www.soinside.com 2019 - 2024. All rights reserved.