说我有以下课程......
@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);
}
如果我最初启动我的应用程序,玩家是正确的,那么当我在同一个会话中,我再次使用新值调用此方法时,玩家不再像您期望的那样为空。但是,似乎没有创建新线程,如果我打开一个新的浏览器窗口(因此创建一个新会话)并调用此方法,它仍然具有上一个会话的值。
为什么@Scope(“session”)没有在线程池中创建新线程?
我已按预期在servlet上下文中指定了<context:component-scan base-package="com." />
,除了服务方法之外,一切正常都可以作为单例,而不是像每个会话创建一个新的线程,比如说Java EE容器。
如果玩家被标记为静态,我会理解。
我还尝试将控制器标记为@Scope("session")
(如下所示),但这似乎也没有任何影响。使我的Spring应用程序为新会话创建新线程的最佳方法是什么?
@Controller
@Scope("session")
public class PlayerController {
答案 0 :(得分:3)
您正在以错误的方式使用@Scope
注释。
引用docs:
当与Component注释一起用作类型级注释时,指示用于注释类型实例的范围的名称。
当与Bean注释一起用作方法级注释时,指示用于从方法返回的实例的范围的名称。
因此,如果您使用的是java配置,则可以注释spring组件bean或创建bean的方法。 Java配置是它甚至编译的唯一原因(它不会在3.0之前的春天)
在你的情况下,注释是在普通的bean方法上,它没有任何意义。
解决正确的问题
看起来您正试图通过将查询结果存储在List<Player> players
来实现数据库缓存。
不要那样做。使用其中一个预先构建的缓存抽象(spring有一个很好的缓存抽象)。
那么@Scope
应该去哪里?
使用@Controller
注释@Scope("session")
无济于事,因为它会创建会话作用域控制器,但它们注入的服务仍然是单例。
仅注释Service bean也不起作用,因为@Controller
是单例,并且它的依赖项在应用程序启动时自动装配。
注释@Service
和@Controller
可能会有效,但看起来有点沉重。
最好避免状态。
答案 1 :(得分:1)
为每个请求创建新线程。
您的服务有一个不是线程安全的实例变量(播放器) - 它由所有线程共享。任何spring bean - 包括控制器和服务默认都是单例,你需要在服务注释上指定它的范围。
@Service("playerService")
@Scope("session")
public class PlayerServiceImpl
但它最好(更简单,更容易扩展)来保持bean单例而不依赖于实例变量(除非它们也由spring / threadsafe / singletons管理)。