【问题标题】:Spring Rest Web Request ScopeSpring Rest Web 请求范围
【发布时间】:2015-08-03 11:41:10
【问题描述】:

我正在为数据库编写一个 Spring REST 类型接口,该接口将检索各种资源的用户特定结果。

为了保持用户,我有一个名为 CurrentUser 的 spring @Component 注释 bean 作为临时措施。

@Component
public class CurrentUser {

    @Autowired
    private UserDAO userDAO;    

    private String userId;
    private String email;
    private String notes;

    public String getUserId() {
        return userId;
    }

    public void setUserId(String userId) throws ApiException {  
        User user = userDAO.getUser(userId) // Database call to 
        if (!user.isValid()) {
            throw ApiException(...)     // The exception would crash back to the user as a error in the response
        }       

        // Valud user so set these aspects. 
        this.userId = user.userId;      
        this.email = user.email;
    }
}

使用以下拦截器在每次调用 API 中的任何方法时在 Spring 拦截器中初始化此对象。

public class AuthenticationInterceptor extends HandlerInterceptorAdapter {
    @Autowired
    private CurrentUser user; 

    @Autowired
    private RequestParameters requestParameters;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws ApiException {
        user.setUserId(StringUtils.defaultString(request.getParameter("userId"), "defaultUser"));
        return true;
    }
} 

这只是一个占位符,用于识别用户,直到可以添加正确的身份验证。

我对 Spring 比较陌生,以及这篇文章的原因 是为了增加我在这种情况下对 Spring 的线程安全方面的理解

我最近发现 Spring 不是自动线程安全的,我可能需要更多地考虑范围。

我想了解的是:

  1. 对于上述设置,是否存在 1000 个并发请求可能相互干扰和覆盖的危险? 例如针对一个用户的请求可能会被不同的用户覆盖,而不是单独的 http 请求,从而导致请求者接收到错误的数据。

  2. 解决此问题的最佳方法是什么。 (即使它会被替换,我也有其他对象以类似的方式实例化) 我正在查看的选项(如果这是一个问题)是设置原型范围,或者直接附加到请求/会话而不是允许它们自己的自动装配对象。

任何人都可以提供给我的任何信息将不胜感激,因为我喜欢一开始就正确(呃),而不是稍后处理错误的假设。

【问题讨论】:

  • 我们甚至有过这样的疑问,并进行了严格的批量测试,发现spring默认不处理基于请求的数据。因此,要么在项目和服务层中启用基于事务的配置。您还可以在 Request 范围内配置 bean,理想情况下,它具有将同步块放入方法中的类似效果。

标签: java multithreading spring rest


【解决方案1】:

答案 1:是的,您不需要 1000 次请求就会遇到麻烦。并行 2 个请求就足够了。

答案 2: 这里的主要问题是范围界定之一:

Spring 托管 bean 的默认范围是 Singleton。这意味着每个应用程序只存在一个 CurrentUser 实例。

这显然不是你想要的。 (因为您在这里遇到了严重的安全问题,每个应用程序只有一个 CurrentUser 实例)。

简单回答:

我可能会使用 Spring Security ;-)

更简单的答案:

如果这不是一个选项:

  • 使用过滤器代替 HandlerInterceptor(更直接地控制清理)

  • 创建一个Thread Local来存储用户(并在Filter中使用finally进行清理)并在Filter中设置

  • 创建一个请求范围的服务(使用@ScopedProxy,以便能够将其连接到单例中),它以 UserService 的形式访问 ThreadLocal(您需要一个接口才能使其轻松工作)

  • 在需要的地方自动装配此 UserService

由于按照规范,Servlet 环境中的每个请求都绑定到一个线程,并且线程局部变量本质上是线程安全的,因此您是完全线程安全的,并且可以很好地扩展。作用域代理的开销很小。

(这只是一个选项,其他选项可以显式使用请求范围或以稍微更优雅的方式使用方面。但是,这是一种相当简单的方法并且可以完成工作。对于更复杂的需求,我会强烈建议研究 Spring Security)。

【讨论】:

  • 正是我想要的。实际的安全实施仍在不断变化,因为它需要连接到 SSO 提供程序。 (我仍然对其中一些有所了解)。这个例子是项目中可能遇到问题的少数 spring bean 之一,因此超出了安全性本身,这个答案很合适。谢谢。
【解决方案2】:

您可以使用 spring mvc 的参数解析器功能,而无需将其设为 bean。 为此实现接口 HandlerMethodArgumentResolver 并将其注册到容器中。然后你的处理方法可以有一个当前用户类型的参数

import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

public class CurrentUserArgumentResolver implements HandlerMethodArgumentResolver{

@Override
public boolean supportsParameter(MethodParameter parameter) {
    if (parameter.getParameterType().equals(CurrentUser.class)) {
        return true;
    }
    return false;
}

@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest,
        WebDataBinderFactory binderFactory) throws Exception {
    if (supportsParameter(parameter)) {
        String userId = (String) webRequest.getAttribute("userId", NativeWebRequest.SCOPE_REQUEST);
        return new CurrentUser(userId);
    }
    return null;
}

public class CurrentUser{

    public CurrentUser(String userId) {
        // TODO Auto-generated constructor stub
    }

}
}

在此之后,您可以拥有 stype 的处理程序方法

@RequestMapping
public String handler(CurrentUser user){
....
}

【讨论】:

    猜你喜欢
    • 2013-01-21
    • 1970-01-01
    • 2011-08-15
    • 2011-09-22
    • 2011-08-19
    • 1970-01-01
    • 1970-01-01
    • 2014-12-14
    • 2011-09-06
    相关资源
    最近更新 更多