1

因此对于一个学校项目,我们创建了一个用户可以提交水下生活报告等的网站。我们使用简单的依赖注入(javax.inject)和错误检查模式如下:Servlets + injection -multithreading的问题

ReportService.java

public interface ReportService { 
    public static enum ReportServiceErrorsENUM { 
     DB_FAILURE, WRONG_COORD // etc 
    } 
    public Set<ReportServiceErrorsENUM> getLastErrors(); 
    public int addNewReport(Report report); 
} 

ReportServiceImpl.java

public class ReportServiceImpl implements ReportService { 

    private Set<ReportServiceErrorsENUM> lastErrors; 
    private @Inject ReportDAO reportDAO; 

    @Override 
    public Set<ReportServiceErrorsENUM> getLastErrors() { 
     return this.lastErrors; 
    } 

    @Override 
    public int addNewReport(Report report) { 
     lastErrors= new HashSet<ReportServiceErrorsENUM>();//throw away previous errors 
     UserInput input = report.getUserInput(); 
     if (input.getLatitude() == null) { 
      addError(ReportServiceErrorsENUM.WRONG_COORD); 
     } 
     // etc etc 
     if (reportDAO.insertReport(report) != 0) { 
      // failure inserting the report in the DB 
      addError(ReportServiceErrorsENUM.DB_ERROR); 
     } 
     if (lastErrors.isEmpty()) // if there were no errors 
      return EXIT_SUCCESS; // 0 
     return EXIT_FAILURE; // 1 
    } 
} 

SubmitReportController.java

@WebServlet("/submitreport") 
public class SubmitReportController extends HttpServlet { 
    private static final long serialVersionUID = 1L; 
    private @Inject ReportService reportService; 

    @Override 
    protected void doPost(HttpServletRequest request, 
    HttpServletResponse response) throws ServletException, IOException { 
     Report report = new Report(); 
     // set the report's fields from the HttpServletRequest attributes 
     if(reportService.addNewReport(report) == ReportService.EXIT_FAILURE) { 
      for(ReportServiceErrorsENUM error : reportService.getLastErrors()) 
       // display the errors etc 
     } else { 
      // display confirmation 
     } 
    } 
} 

的想法是,在Servlet控制器调用服务(将其注入),然后检查服务的返回值,如果有一个错误在服务调用getLastErrors() - 通知什么地方出了错等用户现在我刚刚意识到这不是线程安全的 - 在@ Inject'ed ReportService的(ReportService的)将使用servlet

  1. 是它跨越了所有的线程(共享手指)?
  2. 如何改善这种错误机制?

感谢

回答

1

通常你会想保留这些变量的servlet在一些容器管理的上下文(通常被称为“国家”)。 我会将这些错误移动到请求范围 - 这样它们就被存储在请求对象(概念上),并且任何servlet/jsp /无论在同一个请求上工作的任何人都可以看到/编辑它们。 不同的请求意味着不同的数据存储。使用请求范围从一个servlet

示例代码可以在这里找到:http://www.exampledepot.com/egs/javax.servlet/State.html

+0

不幸的是,这套错误属于服务类 - 我没有看到从那里获取请求的方式(?) – 2012-07-22 19:09:56

+0

处理此问题的最简单方法如下所示: class ServiceState {private Set lastErrors} 然后在你的servlet中--ServiceState stateForThisRequest = new ...; ReportService。addNewReport(report,stateForThisRequest); 之后你添加stateForThisRequest请求(在一些已知的字符串键)供任何其他处理该请求使用(我假设你有一个jsp或东西的显示?) – radai 2012-07-22 19:16:58

+0

有可能某种方式来注入一些事务或会话上下文到你的服务中(不真正知道CDI - javax.inject),但这样就足够了。像Tomasz说的 - 在同一个会话上可能有多个请求 - 这就是为什么请求范围最适合保持这个请求。 – radai 2012-07-22 19:19:06

1

你的设计是线程安全的,也没有准备好服务器的多个用户都不是。它不是线程安全的,因为多个用户(浏览器)可以同时访问该servlet,并同时访问lastErrors。 (是的,只有一个servlet实例和你的服务)。 HashSet你使用的是而不是线程安全。

此外,如果两个不同的人试图使用相同的应用程序,他们将覆盖并有权访问彼此提交的报告(错误)。换句话说,所有用户都共享全局状态,而每个用户/会话应该有一个状态。

通过修复第二个问题(我给了你一个提示:使用HTTPSession),你不可能看到第一个问题。这是因为很难看到同时访问同一个会话。但它是可能的(并发AJAX请求,两个浏览器标签)。记住这一点,但现在要解决更重要的问题。

+0

他可以使用ConcurrentSet来绕过第一个问题。 – 2012-07-22 19:19:32

+0

您是否可以辨别出将服务中的共享变量(基本上是Set lastErrors)移动到Servlet请求范围的方法,如其他答案中所建议的那样是理想的。请记住有很多服务使用这种模式。即使我使用同步方法,如果用户A称为addReport,并且此返回的用户B可以在用户A调用getLastErrors()(race)之前调用addReport和clear()该错误集。而粗暴的同步只会杀死多线程的好处。另外我不确定我是否得到了两个场景之间的区别 – 2012-07-22 19:19:48

+0

@AmirPashazadeh:请参阅http://stackoverflow.com/questions/6992608 – 2012-07-22 19:30:02