【问题标题】:SpringMVC @Scope session not creating threadsSpringMVC @Scope 会话不创建线程
【发布时间】:2013-05-06 19:59:45
【问题描述】:

假设我有以下课程......

@Controller
public class WebController {
    @Autowired PersonService personService;


    @RequestMapping(value = "/get", method = RequestMethod.GET)
    @ResponseBody
    @Scope("session")
    public List<Player> getPerson(String personName) {
        return playerService.getByName(personName);
    }
}

现在这会调用以下服务...

@Service("playerService")
public class PlayerServiceImpl implements PlayerService {
private List<Player> players;
@Override
    @Transactional
    public List<Player> getByName(final String name) {
        if (players == null) {
            players = getAll();
        }
        return getValidPlayers(name);
    }

如果我最初启动我的应用程序,players 正确地为空,然后在同一个会话中,我再次使用新值调用此方法,players 不再为空,正如您所期望的那样。但是,似乎没有创建新线程,如果我打开一个新的浏览器窗口(因此创建一个新会话)并调用此方法,它仍然具有上一个会话的值。

为什么@Scope("session") 没有在线程池中创建新线程?

我已经按预期在我的 servlet-context 中指定了 &lt;context:component-scan base-package="com." /&gt;,除了服务方法都充当单例而不是像 Java EE 容器那样为每个会话创建一个新线程之外,一切正常。

如果玩家被标记为静态,我会理解。

我也尝试将我的控制器标记为@Scope("session")(如下所示),但这似乎也没有影响。让我的 Spring 应用为新会话创建新线程的最佳方法是什么?

@Controller
@Scope("session")
public class PlayerController {

【问题讨论】:

  • 除了灵魂检查的回答之外,您还有几点错误。会话不等同于线程(在同步 servlet 容器中,线程为请求提供服务,但同一会话的请求可能由不同的线程提供服务;无论应用程序是有状态还是无状态,都使用线程)。而一个新的浏览器窗口只会在隐身模式下创建一个新会话,否则 cookie 是共享的,您显然会获得相同的会话。
  • @NimChimpsky 哈哈,现在我才查看该链接。本可以为我节省一些打字时间:)
  • @FrankPavageau 对不起,我应该说,当我在不同的浏览器中打开一个新的浏览器窗口时,比如在 IE 中打开一个新的浏览器窗口,在 Chrome 中打开一个,会话不共享。

标签: java spring


【解决方案1】:

你用错了@Scope注解。

引用docs

当与 Component 注释一起用作类型级注释时,指示用于注释类型实例的范围名称。

当与 Bean 注解一起用作方法级注解时,表示用于从方法返回的实例的范围名称。

因此,如果您使用 java 配置,您可以注释 Spring 组件 bean 或创建 bean 的方法。 Java 配置是它编译的唯一原因(它不会在 pre 3.0 spring)

在你的情况下,注释是在一个普通的 bean 方法上,它没有任何意义。

解决正确的问题

您似乎正在尝试通过将查询结果存储在List&lt;Player&gt; players 中来实现数据库缓存。

不要那样做。改用其中一种预构建的缓存抽象(spring 有一个非常好的)。

那么@Scope应该去哪里呢?

@Scope("session") 注释@Controller 将无济于事,因为它会创建会话范围的控制器,但它们注入的服务仍然是单例。

仅注释 Service bean 也不起作用,因为 @Controller 是一个单例,它的依赖关系在应用程序启动时自动装配。

同时注释@Service@Controller 可能有效,但似乎有点笨拙。

最好完全避免状态。

【讨论】:

  • 好吧——我将如何修改我的 spring 控制器以便为每个会话创建一个新线程?我尝试在控制器上添加@Scope("session"),但这也没有影响 - 更新了问题。
  • @david99world 查看更新,我认为您正在尝试解决错误的问题
  • 就缓存问题,我用的是Hibernates的二级缓存,还是用基于Spring的更好吗?
  • @david99world 这真的取决于,但这种情况下休眠应该足够了
  • 是的,我将其更改为在内存中使用的原因是这似乎比使用 Hibernates 二级缓存更快。我应该永远不要使用局部变量,以便我的应用程序始终是线程安全的吗?
【解决方案2】:

为每个请求创建新线程。

您的服务有一个非线程安全的实例变量 (players) - 它由所有线程共享。任何 spring bean - 包括控制器和服务默认都是单例的,您需要在服务注释上指定其范围。

@Service("playerService")
@Scope("session")
public class PlayerServiceImpl 

但最好(更简单,更容易扩展)保持 bean 单例而不依赖实例变量(除非它们也由 spring/threadsafe/singleton 管理)。

【讨论】:

  • 啊,好吧 - 如果将 bean 保持为单例是最好和更安全的(我假设就像我在上面所做的那样,没有范围注释),那么是否没有两个用户可能同时登录的风险并且错误的用户可能会收到不正确的数据,因为变量可能是从其他人的线程中编辑的?
  • 另外 - 假设我有一些复杂的业务逻辑,如果我有点期待把它放在我的服务层但现在知道我不能使用实例变量,应该把它交给 DAO等级? DAO 级别具有实例变量是否安全?如果不是,那么在保持线程安全(允许类级别变量)的同时,将我的业务逻辑放在哪里最好?
猜你喜欢
  • 1970-01-01
  • 2016-06-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-08-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多