我在2个控制器上使用@SessionAttributes并且遇到了一些非常奇怪的行为。我的第一个控制器(ViewController)只是一个显示JSP页面的视图控制器。另一个是处理Ajax请求的控制器(AjaxController)。我有一个session属性,它只是一个HashMap作为成员的对象。该对象是地图周围的包装器。地图从数据库填充并放入会话中,通过ViewController显示正常。但是,当我通过ajax请求(AjaxController)从地图中删除并刷新页面时,ViewController SOMETIMES显示该元素已被删除,但有时元素仍然存在。这是代码片段:
ViewController (主页只显示userSettings包含的地图内容
@Controller
@SessionAttributes({"userSettings"})
public class ViewController {
@RequestMapping(value="/", method=RequestMethod.GET)
public String home(ModelMap model) {
UserSettings userSettings = (UserSettings) model.get("userSettings");
String userListenersJson = userSettings.toJson(); // for bootsrtapping the js on the front end
return "views/home";
}
}
AjaxController:
@Controller
@SessionAttributes({"userSettings"})
public class AjaxController {
@RequestMapping(value="/users/listeners/{externalId}", method=RequestMethod.DELETE)
public @ResponseBody
AjaxResponse<?> deleteListener(ModelMap model,
@PathVariable long externalId) {
UserSettings userSettings = (UserSettings) model.get("userSettings");
userSettings.removeSetting(externalId);
return new AjaxResponse<String>(null, true);
}
}
我在这里使用@SessionAttributes错了吗?为什么这有时会工作而不是其他人?我也尝试将所有视图和ajax功能放在同一个控制器中并经历相同的行为。
感谢您的帮助!
修改
我通过springsecurity使用UserPrincipal重构了我的代码。我的理解是这个对象存储在会话中。无论如何,我看到了完全相同的行为。
这是填充用户设置图的UserPrincipal构造函数。我在这里设置断点以确保设置正确的listenerDBO - 它们每次都是。这是侦听器从db设置到CustomUserPrincipal中的UserSettings对象的唯一时间。所有其他添加/删除都是通过控制器完成的(快速放弃:添加永不失败......只删除):
public CustomUserPrincipal(UserDBO userDBO) {
// set UserSettings obj
UserSettingsAdapter.addListeners(userDBO.getUserListenerDBOs(), userSettings);
}
UserSettings对象本身:
public class UserSettings implements Serializable {
private static final long serialVersionUID = -1882864351438544088L;
private static final Logger log = Logger.getLogger(UserSettings.class);
private Map<Long, Listener> userListeners = Collections.synchronizedMap(new HashMap<Long, Listener>(1));
// get the listeners as an arraylist
public List<Listener> userListeners() {
return new ArrayList<Listener>(userListeners.values());
}
public Map<Long, Listener> getUserListeners() {
return userListeners;
}
public Listener addListener(Listener listener) {
userListeners.put(listener.getId(), listener);
return listener;
}
// I'm logging here to try and debug the issue. I do see the success
// message each time this function is called
public Listener removeListener(Long id) {
Listener l = userListeners.remove(id);
if (l == null) {
log.info("failed to remove listener with id " + id);
} else {
log.info("successfully removed listener with id " + id);
}
log.info("Resulting map: " + userListeners.toString());
log.info("Map hashcode: " + userListeners.hashCode());
return l;
}
public Listener getListener(long id) {
return userListeners.get(id);
}
}
这是UserSettingsAdapter类中的辅助函数,它添加到UserSettings对象,从CustomUserDetails构造函数调用:
public static void addListeners(Set<UserListenerDBO> userListeners, UserSettings userSettings) {
for (UserListenerDBO userListenerDBO : userListeners) {
if (userListenerDBO.isActive()) {
addListener(userListenerDBO, userSettings);
}
}
}
我还将控制器代码更改为使用CustomUserPrincipal对象而不是@SessionAttributes:
在ViewController中:
@RequestMapping(value="/", method=RequestMethod.GET)
public String home(ModelMap model) {
CustomUserPrincipal userPrincipal = authenticationHelpers.getUserPrincipal();
UserSettings userSettings = userPrincipal.getUserSettings();
String userListenersJson = userSettings.toJson();
return "views/home";
}
在AjaxController中:
@RequestMapping(value="/users/listeners/{externalId}", method=RequestMethod.DELETE)
public @ResponseBody
AjaxResponse<?> deleteListener(ModelMap model,
@PathVariable long externalId) {
CustomUserPrincipal userPrincipal = authenticationHelpers.getUserPrincipal();
UserSettings userSettings = userPrincipal.getUserSettings();
userSettings.removeListener(externalId);
return new AjaxResponse<String>(null, true);
}
我希望这有助于解决这个问题!
答案 0 :(得分:0)
我遇到了与@SessionAttributes类似的问题。控制器在类级别具有@SessionAttributes注释,其中一个方法处理POST请求,并包含会话管理对象的实例作为参数。此实例已保存到数据库,但后续请求重复使用,导致某些数据损坏。我们必须添加另一个类型为SessionStatus的方法参数,并调用SessionStatus.setComplete()
。这导致实例从会话中删除,并阻止重用和损坏。因此,尝试将SessionStatus实例添加到控制器的处理程序方法中,并在适当的时候调用setComplete()。
isComplete()
;我的意思是引用setter setComplete()
。
答案 1 :(得分:0)
@SessionAttributes特定于Controller,不在多个控制器之间共享。 相反,请考虑手动使用session.setAttribute(类HttpSession)。
你应该看看这里:http://beholdtheapocalypse.blogspot.fr/2013/01/spring-mvc-framework-sessionattributes.html