【问题标题】:Spring MVC 3 for @Autowired Singleton Thread-Safety用于@Autowired Singleton 线程安全的 Spring MVC 3
【发布时间】:2012-11-26 15:53:33
【问题描述】:

嗯,我在 MVC Spring 3 设计中从 Controller->Service->Repository 有 3 层。现在,我的问题是,由于默认范围定义为 Singleton,它们是线程安全的吗?

代码如下:

UserController.java

@Controller
@RequestMapping("/users")
public class UserController extends ExceptionExtension {
     @Autowired
     private IUserService userService;

     @RequestMapping(value = { "/update", "/update/" }, method = RequestMethod.GET)
     public String updateUser(@RequestParam("email") String eMail, ModelMap model)
        throws Exception {

          if (eMail.isEmpty() || eMail == null) {
              throw new ArgumentIsEmptyException("Required String parameter 'email' is empty");
          } else {
              UserModel userModel = userService.setUser(eMail);

              if (userModel != null) {
                  model.put("roleList", userService.setRoleList());
                  model.put("title", "Update Existing User");
                  model.put("post", "/users/post/update");
                  model.put("userForm", userModel);

                  return "users.update";
              } else {
                  model.put("title", "Update Existing User");
                  model.put("result", "<font color='red'><u>" + eMail + "</u> does not exist in the database.</font>");
                  model.put("flag", "Error");

                  return "users.result";
              }
          }
     }
}

用户服务.java

public class UserService implements IUserService {

    @Autowired
    private IUserManager userManager;

    public UserModel setUser(String eMail) {
        UserModel userModel = new UserModel();
        Entity userEntity = userManager.getUser(eMail);

        if (userEntity != null) {
            userModel.setEMail(eMail);
            userModel.setRole(userEntity.getProperty("role").toString());
            userModel.setEnable((Boolean)userEntity.getProperty("enable"));

            return userModel;
        } else {
            return null;
        }
    }
}

假设用户A和用户B同时运行相同的url但参数不同。
用户 A 请求 => "http://domain.com/users/update?user=myname1@domain.com"。
用户 B 请求 => "http://domain.com/users/update?user=myname2@domain.com"。

由于控制器是单例的,用户 A 的电子邮件变量是否会与用户 B 重叠,反之亦然?

我很难理解单例线程安全在这种情况下是如何工作的。我的 @Service 和 @Repository 是否应该声明为 @Scope("prototype) 以便内部方法变量与新实例隔离?

====>

使用 Scope("request") 到我的@Service 层,我遇到了这个错误消息:/

3328 [main] 错误 org.springframework.web.context.ContextLoader - 上下文初始化失败 org.springframework.beans.factory.BeanCreationException:错误 创建名为“roleController”的bean:注入自动装配 依赖失败;嵌套异常是 org.springframework.beans.factory.BeanCreationException:不能 自动装配字段:私有 com.company.dashboard.service.IRoleService com.company.dashboard.controller.RoleController.roleService;嵌套的 例外是 org.springframework.beans.factory.BeanCreationException: 创建名为“roleService”的 bean 时出错:范围“请求”不是 对当前线程有效;考虑为 如果您打算从单例中引用此 bean;嵌套的 异常是 java.lang.IllegalStateException: No thread-bound request 发现:您指的是实际之外的请求属性吗? Web 请求,或处理原始请求之外的请求 接收线程?如果您实际上是在 Web 请求中操作 仍然收到此消息,您的代码可能在外面运行 DispatcherServlet/DispatcherPortlet:在这种情况下,使用 RequestContextListener 或 RequestContextFilter 暴露当前 请求。

【问题讨论】:

    标签: java google-app-engine spring-mvc thread-safety singleton


    【解决方案1】:

    这里的线程安全取决于userService的内部锁定,以防作用域singleton

    这最终会发生 => 相同的 userSpace bean 用于两个请求,并且两个请求同时访问 userService bean。所以IUserService 需要是线程安全的,整个操作才能正常执行。

    或者

    将范围设置为request

    在这种情况下,为每个请求分配一个新 bean,并且由于线程限制,整个操作是线程安全的。只要您在IUserSpace中进行相应编码

    【讨论】:

    • 我在 UserService 中没有私有变量,但它 @Autowired 到 IUserManager 存储库。
    • 那么,基本上我需要将 Scope("request") 添加到 Service 和 Repository 层?
    • 对 IUserService 的范围请求导致这种情况?
    • 对 IUserService 有范围请求
    • 是的,线程安全对于单例设计非常重要。否则,一切都会一团糟……
    【解决方案2】:

    如果您创建任何层范围:原型,就像 cmets 中建议的那样,您只是将问题再次转移到下面的层。

    您需要决定要在哪里划分事务。通常,这是在对服务的调用级别完成的。 Spring 可以为您处理。

    控制器、服务和存储库通常应该以无状态方式实现,也就是说,这些类没有任何您的方法可以修改的实例变量。否则你将变得容易受到竞争条件的影响。最后,您的数据存储必须支持使用事务。

    您使用 Google Appengine 数据存储。与任何 RDBMS 不同,它有一种非常具体的事务处理方法。要从 Java 应用程序与 appengine 交谈,我推荐使用“Objectify”。首先阅读[关于它的概念](以及底层 BigTable 数据存储的概念1

    【讨论】:

    • 感谢您回复我的帖子。显然,您看到我的代码在 Controller、Service 和 Repository 类中没有任何实例变量。我的实现是带有 MVC Spring 3 的 Google App Engine。我不确定 MVC Spring 是否可以帮助我在服务层处理事务。因此,当我按照 Google App Engine 示例进行编码时,我在数据存储/存储库层实现了事务。我真正担心的是我在第一篇文章中提到的并发更新问题。我只是想确保用户的数据不会相互交叉。
    • 我还有一个关于 Autowired 变量的问题。类中的 Autowired 变量是否被视为实例变量?
    • 是的,所以除非该对象的方法以线程安全的方式实现,否则它会受到竞争条件的影响。我编辑了我的答案,以考虑您使用谷歌应用引擎,这完全改变了事情。
    • 我有自己的方法来控制存储库层的事务。到目前为止一切都很好。 GAE 提到过线程安全,但它的线程安全仅适用于静态方法。虽然我使用非静态方法对所有层进行了编码,但我认为它们只是非线程安全的。当类 Autowired 变量同样是一个实例变量时,问题就出现了,我必须在类变量实例化时更改范围以请求。
    • 我读了很多 Objectify。我觉得这很棒,但我仍然更喜欢使用我自己的 Google Datastore 低级 API 方法(例如 UserManager)来控制存储库层。
    猜你喜欢
    • 1970-01-01
    • 2013-03-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多